﻿/// <reference path="../jqGrid/jqGrid-4.6.0/i18n/grid.locale-en.js" />
/// <reference path="../jqGrid/jqGrid-4.6.0/jquery.jqGrid.src.js" />
/// <reference path="../jQuery/jquery-1.11.0.js" />
/// <reference path="../jQuery-UI/jquery-ui-1.10.4.custom.js" />

///Author : Henri d'Orgeval

(function (window, $, undefined) {
    try {
        var vpGrid = window.vpGrid || {};
        vpGrid.version = "0.5.0";

        //private options
        // to override these options : call the vpGrid.init method with an option parameter
        var vpGridOptions = {
            enableRemoteLogging: false,
            enableFilterToolbar: true,
            enableOperatorsInFilterToolbar: false,
            enableMultiSelect: false,
            defaultCaption: "Default caption",
            enableCustomCaption: false,
            enableCustomStatusBar: false,
            loadGridOnPageLoad: true,
            minColWidth: 60,
            fitToBrowserWidth: true,
            fixedColWidth:false,
            datepickerOptions: {
                showOtherMonths: true,
                selectOtherMonths: true
            }
        }

        var vpGridAjaxOptions = {
            error: logAjaxError,
            statusCode: {
                400: function () {
                    logAjaxHttpStatusCode(400)
                },
                401: function () {
                    logAjaxHttpStatusCode(401)
                },
                403: function () {
                    logAjaxHttpStatusCode(403)
                },
                500: function () {
                    logAjaxHttpStatusCode(500)
                },
                503: function () {
                    logAjaxHttpStatusCode(503)
                },
                0: function () {
                    logAjaxHttpStatusCode(0)
                }
            }
        }

        // Public Data attributes used by vpGrid
        // data tags attributes are used to inject any kind of behavior within the Grid
        vpGrid.HtmlDataTag = vpGrid.HtmlDataTag || {};
        vpGrid.HtmlDataTag.TimelineSequence = "data-vp-grid-timeline-sequence";
        vpGrid.HtmlDataTag.DataSource = "data-vp-grid-datasource";
        vpGrid.HtmlDataTag.DataSourceType = "data-vp-grid-datasource-type";
        vpGrid.HtmlDataTag.Url = "data-vp-grid-url";
        vpGrid.HtmlDataTag.UIElement = "data-vp-grid-ui-element";
        vpGrid.HtmlDataTag.Handlers = "data-vp-grid-handlers";
        vpGrid.HtmlDataTag.HandlersRegistered = "data-vp-grid-handlers-registered";
        vpGrid.HtmlDataTag.UIDisabled = "data-vp-grid-ui-disabled";
        vpGrid.HtmlDataTag.UIIdentifier = "data-vp-grid-ui-id";

        // This Data tag is used to know if the current element has been attached to its container. For exemple the element is defined outside its container and then placed at a specific time at a specific place in the DOM.
        vpGrid.HtmlDataTag.UIInPlace = "data-vp-grid-ui-inplace";

        vpGrid.HtmlDataTag.UIContentPlaceholder = "data-vp-grid-ui-content-placeholder";


        //default values for data tag
        vpGrid.HtmlDataTag.DefaultValue = vpGrid.HtmlDataTag.DefaultValue || {};
        vpGrid.HtmlDataTag.DefaultValue.DataSourceType = "web";


        //data tags for security trimming managment
        vpGrid.HtmlDataTag.securityTrimmed = "data-vp-grid-security-trimmed";
        vpGrid.HtmlDataTag.securityTrimmedRoles = "data-vp-grid-security-trimmed-roles";
        vpGrid.HtmlDataTag.securityTrimmedUrl = "data-vp-grid-security-trimmed-url";
        vpGrid.HtmlDataTag.securityTrimmedSequence = "data-vp-grid-security-trimmed-sequence";
        vpGrid.HtmlDataTag.securityTrimmedRolesType = "data-vp-grid-security-trimmed-roles-type";

        //data tags for the creation and the configuration of the grid columns
        vpGrid.HtmlDataTag.ColumnName = "data-vp-grid-column-name";
        vpGrid.HtmlDataTag.ColumnWidth = "data-vp-grid-column-width";
        vpGrid.HtmlDataTag.ColumnHidden = "data-vp-grid-column-hidden";
        vpGrid.HtmlDataTag.ColumnEditable = "data-vp-grid-column-editable";
        vpGrid.HtmlDataTag.ColumnEditType = "data-vp-grid-column-edittype";
        vpGrid.HtmlDataTag.ColumnAlign = "data-vp-grid-column-align";
        vpGrid.HtmlDataTag.ColumnType = "data-vp-grid-column-type";
        vpGrid.HtmlDataTag.ColumnCSS = "data-vp-grid-column-css";
        vpGrid.HtmlDataTag.ColumnFrozen = "data-vp-grid-column-frozen";
        vpGrid.HtmlDataTag.ColumnMinValue = "data-vp-grid-column-min-value";
        vpGrid.HtmlDataTag.ColumnMaxValue = "data-vp-grid-column-max-value";

        //data tags for automatic creation and configuration of jQuery-UI widgets
        vpGrid.HtmlDataTag.DialogId = "data-vp-grid-dialog-id";
        vpGrid.HtmlDataTag.UIWidget = "data-vp-grid-ui-widget";
        vpGrid.HtmlDataTag.UIWidgetOptions = "data-vp-grid-ui-widget-options";

        //data tags for remote filtering
        vpGrid.HtmlDataTag.RemoteDataFilterKey = "data-vp-grid-remote-data-filter-key";
        vpGrid.HtmlDataTag.RemoteDataFilterName = "data-vp-grid-remote-data-filter-name";
        vpGrid.HtmlDataTag.RemoteDataFilterOperator = "data-vp-grid-remote-data-filter-operator";

        //data tags for backup/restore/augmentation
        vpGrid.HtmlDataTag.CssToAdd = "data-vp-grid-css-to-add";
        vpGrid.HtmlDataTag.OriginalClassAttributeContent = "data-vp-grid-original-class-attribute-content";

        //public Role attributes used by vpGrid
        // Role attributes are used by vpGrid to locate specific parts of the grid or elements used by the grid.
        vpGrid.HtmlRoleAttribute = vpGrid.HtmlRoleAttribute || {};
        vpGrid.HtmlRoleAttribute.vpGridContainer = "vp-grid-container";
        vpGrid.HtmlRoleAttribute.vpGridBody = "vp-grid-body";
        vpGrid.HtmlRoleAttribute.vpGridSchema = "vp-grid-schema";
        vpGrid.HtmlRoleAttribute.vpGridSchemaColumns = "vp-grid-schema-columns"
        vpGrid.HtmlRoleAttribute.vpGridSchemaCaption = "vp-grid-schema-caption";
        vpGrid.HtmlRoleAttribute.vpGridSchemaColumnsGroup = "vp-grid-schema-columns-group";
        vpGrid.HtmlRoleAttribute.vpGridSchemaStatusBar = "vp-grid-schema-status-bar";
        vpGrid.HtmlRoleAttribute.Titlebar = "vp-grid-dialog-title-bar";
        vpGrid.HtmlRoleAttribute.vpGridModalDialog = "vp-grid-modal-dialog";
        vpGrid.HtmlRoleAttribute.RemoteDataFilter = "vp-grid-remote-data-filter";
        vpGrid.HtmlRoleAttribute.ActiveHeader = "vp-grid-active-header";

        vpGrid.HtmlRoleAttribute.vpGridPartialUI = "vp-grid-partial-ui";

        //trace API
        function logExceptionToConsole(ex) {
            try {
                if (ex === undefined) {
                    return;
                }

                if (console === undefined) {
                    return;
                }

                if (console.error === undefined) {
                    return;
                }

                var smalldateTime = new Date().toSmallDateTime();
                var msg = "[" + smalldateTime + "] ";

                if (ex.stack) {
                    msg += ex.stack;
                    console.error(msg);
                    return;
                }

                if (ex.message) {
                    msg += ex.message;
                }

                console.error(msg);

            } catch (e) {
                var msg = e.message;
            }
        }

        function sendExceptionToRemote(ex) {
            try {
                //check if sending messages to remote server is enabled
                if (!vpGridOptions.enableRemoteLogging) {
                    return;
                }

                //get url to be requested
                var crudOperations = vpGridOptions.crudOptions;
                if (isNullOrUndefinedOrEmpty(crudOperations)) {
                    vpGrid.logError({
                        message: "CRUD operations are not defined in the vpGrid Options object. Errors cannot be sent to remote server."
                    })
                    return;
                }

                if (isNullOrUndefinedOrEmpty(crudOperations.trace)) {
                    vpGrid.logError({
                        message: "CRUD option trace is not defined in the vpGrid Options object. Errors cannot be sent to remote server."
                    })
                    return;
                }

                var requestUrl = crudOperations.trace;
                requestUrl += '&error=' + encodeURIComponent(JSON.stringify(ex));
                var img = new Image();
                img.src = requestUrl;

            } catch (e) {
                logExceptionToConsole(e);
            }
        }

        vpGrid.logException = function (ex) {
            /// <signature>
            /// <summary>Log exception on all configured output devices (Console, Remote Server, custom DOM element</summary>
            /// <param name="ex" type="Object">exception object</param>
            /// </signature>
            try {
                logExceptionToConsole(ex);

                sendExceptionToRemote(ex);

                //TODO : log inside specific DOM element

            } catch (e) {
                logExceptionToConsole(e)
            }
        }

        function logErrorToConsole(err) {
            try {
                if (err === undefined) {
                    return;
                }

                if (console === undefined) {
                    return;
                }

                if (console.error === undefined) {
                    return;
                }

                var smalldateTime = new Date().toSmallDateTime();
                var msg = "[" + smalldateTime + "] ";

                if (err.functionName) {
                    msg += "[function: " + err.functionName + "] ";
                }

                if (err.callerName) {
                    msg += "[caller name: " + err.callerName + "] ";
                }

                if (err.message) {
                    msg += err.message;
                }

                if (err.info) {
                    msg += '\n' + err.info;
                }

                //if (err.callerCode) {
                //    msg += "\n[caller code: " + err.callerCode + "] ";
                //}

                console.error(msg);

            } catch (e) {
                logExceptionToConsole(e)
            }
        }

        vpGrid.logError = function (err) {
            /// <signature>
            /// <summary>Log application error on all configured output devices (Console, Remote Server, custom DOM element</summary>
            /// <param name="err" type="Object">literal object that is like { message : "xxx", functionName : "yyy", info : "ttt" }</param>
            /// </signature>
            try {
                // try find caller
                try {
                    var caller = vpGrid.logError.caller;
                    var callerName = caller.name.toString();
                    var callerCode = caller.toString();
                    err.callerName = callerName;
                    err.callerCode = callerCode;

                } catch (e) {

                }

                logErrorToConsole(err);

                //TODO : remote logging

                //TODO : log inside specific DOM element

            } catch (e) {
                vpGrid.logException(e)
            }
        }

        function logAjaxErrorToConsole(xhr, statusMessage, errorMessage) {
            try {
                if (xhr === undefined) {
                    return;
                }

                if (console === undefined) {
                    return;
                }

                if (console.error === undefined) {
                    return;
                }

                var smalldateTime = new Date().toSmallDateTime();
                var msg = "[" + smalldateTime + "] ";
                if (statusMessage) {
                    msg += statusMessage;
                }

                if (errorMessage) {
                    msg += "; " + errorMessage;
                }

                console.error(msg);

            } catch (e) {
                //TODO : log
                var msg = e.message;
            }
        }
        function logAjaxError(xhr, statusMessage, errorMessage) {
            try {
                logAjaxErrorToConsole(xhr, statusMessage, errorMessage);

                //TODO : remote logging

                //TODO : log inside specific DOM element

            } catch (e) {
                vpGrid.logException(e)
            }
        }

        function logAjaxHttpStatusCode(statusCode) {
            try {
                if (isNullOrUndefined(statusCode)) {
                    return;
                }

                var statusErrorMap = {
                    0: "Cross Domain call is rejected by the browser (see CORS configuration)",
                    400: "Server understood the request but request content was invalid.",
                    401: "Unauthorised access.",
                    403: "Forbidden resouce can't be accessed",
                    500: "Internal Server Error.",
                    503: "Service Unavailable"
                };
                var message = "Http status code : " + statusCode + "; ";
                if (statusCode in statusErrorMap) {
                    message += statusErrorMap[statusCode];
                }
                vpGrid.logError({ message: message });

            } catch (e) {
                vpGrid.logException(e)
            }
        }

        function logServerError(response, details, element) {
            try {
                if (isNullOrUndefined(response)) {
                    //TODO : log
                    return;
                }

                var errorMessage = "";
                if (isNotNullOrUndefinedOrEmpty(response.errorMessage)) {
                    errorMessage = response.errorMessage;
                }

                var detailsMessage = "";
                if (isNotNullOrUndefinedOrEmpty(details)) {
                    detailsMessage = details;
                }

                var domHTML = outerHtml(element);

                var message = "Server Error : " + errorMessage + "; " + detailsMessage + "; on element: \n" + domHTML;
                vpGrid.logError({ message: message });

            } catch (e) {
                vpGrid.logException(e)
            }
        }
        // end trace API


        // Javascript Types Extensions
        //TODO : check if prototypes already exist
        //Date extensions
        (function () {
            try {
                var test = new Date();
                if (test.toSmallDateTime) {
                    vpGrid.logError({
                        message: "Date.prototype.toSmallDateTime is already defined. Check that vpGrid is not loaded twice or that any third-party JavaScript library has already defined this method."
                    })
                    return;
                }

                Date.prototype.toSmallDateTime = function () {
                    try {
                        var dd = this.getDate();
                        var mm = this.getMonth() + 1;
                        var yyyy = this.getFullYear();
                        var hh = this.getHours();
                        var mn = this.getMinutes();
                        var ss = this.getSeconds();

                        if (dd < 10) {
                            dd = '0' + dd;
                        }
                        if (mm < 10) {
                            mm = '0' + mm;
                        }

                        if (hh < 10) {
                            hh = '0' + hh;
                        }

                        if (mn < 10) {
                            mn = '0' + mn;
                        }

                        if (ss < 10) {
                            ss = '0' + ss;
                        }

                        var now = yyyy + '/' + mm + '/' + dd + ' ' + hh + ':' + mn + ':' + ss;
                        return now;

                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);
                        return this;
                    }
                }

            } catch (e) {
                vpGrid.logException(e)
            }
        })();

        //Array extensions
        Array.prototype.toUnique = function () {
            try {

                if (this.length === 0) {
                    return this;
                }

                if (this.length === 1) {
                    return this;
                }


                var result = [], map = {}, item;
                var itemCount = this.length;
                for (var i = 0; i < itemCount; i++) {
                    item = this[i];
                    if (!map[item]) {
                        result.push(item);
                        map[item] = true;
                    }
                }

                return result;


            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        Array.prototype.removeEmptyEntries = function () {
            try {

                if (this.length === 0) {
                    return this;
                }

                var result = []
                var itemCount = this.length;
                for (var i = 0; i < itemCount; i++) {
                    var item = this[i];
                    if (isNotNullOrUndefinedOrEmpty(item)) {
                        result.push(item);
                    }
                }

                return result;

            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        Array.prototype.clear = function () {
            try {

                if (this.length === 0) {
                    return this;
                }

                var itemCount = this.length;
                for (var i = 0; i < itemCount; i++) {
                    this.shift();
                }

                return this;

            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        Array.prototype.pushOnce = function (input) {
            try {

                if (isNullOrUndefinedOrEmpty(input)) {
                    //TODO : log
                    return;
                }

                if (this.length === 0) {
                    this.push(input);
                    return;
                }

                var itemCount = this.length;
                for (var i = 0; i < itemCount; i++) {
                    var item = this[i];
                    if (item === input) {
                        return;
                    }
                }
                this.push(input);

            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        Array.prototype.isEmpty = function () {
            try {

                if (this.length === 0) {
                    return true;
                }

                var itemCount = this.length;
                for (var i = 0; i < itemCount; i++) {
                    var item = this[i];
                    if (isNotNullOrUndefinedOrEmpty(item)) {
                        return false;
                    }
                }

                return true;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }
        // end Array extensions

        //String extensions
        String.prototype.isNullOrEmpty = function () {
            try {
                if (this === null) {
                    return true;
                }

                if (this === undefined) {
                    return true;
                }

                if (this.length === undefined) {
                    return true;
                }

                if (this.length === 0) {
                    return true;
                }

                return false;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.isNullOrEmptyOrWhitespace = function () {
            try {
                if (this === null) {
                    return true;
                }

                if (this === undefined) {
                    return true;
                }

                if (this.length === undefined) {
                    return true;
                }

                if (this.length === 0) {
                    return true;
                }

                if (this.trim === undefined) {
                    String.prototype.trim = function () { return this.replace(/^\s+|\s+$/g, ''); };
                }

                var trimedInput = this.trim();

                if (trimedInput === null) {
                    return true;
                }

                if (trimedInput === undefined) {
                    return true;
                }

                if (trimedInput.length === undefined) {
                    return true;
                }

                if (trimedInput.length === 0) {
                    return true;
                }

                return false;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.isNotNullOrEmptyOrWhitespace = function () {
            return this.isNullOrEmptyOrWhitespace() === false;
        }

        String.prototype.toEncodedSymbols = function () {
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return "";
                }

                var encoded = this.replace("ô", "&#244;");
                return encoded;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return this;
            }
        }

        String.prototype.toEncodedHTML = function () {
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return "";
                }

                var htmlEncodedInput = $('<div/>').text(this).html();
                //htmlEncodedInput = htmlEncodedInput.toEncodedSymbols();

                return htmlEncodedInput;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return this;
            }
        }

        String.prototype.toArray = function (splitter) {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return [];
                }
                if (isNullOrUndefinedOrEmpty(splitter)) {
                    splitter = ",";
                }

                var splittedArray = this.split(splitter);

                if (isNullOrUndefinedOrEmpty(splittedArray)) {
                    return [];
                }

                // trim every item and remove empty items
                var itemCount = splittedArray.length;
                var normalizedArray = [];
                for (var i = 0; i < itemCount; i++) {
                    var item = splittedArray[i];
                    item = $.trim(item);

                    if (isNotString(item)) {
                        continue;
                    }

                    if (item.isNullOrEmptyOrWhitespace()) {
                        continue;
                    }
                    normalizedArray.push(item);
                }

                return normalizedArray;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return [];
            }
        }

        String.prototype.toInt = function () {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return NaN;
                }

                var intValue = parseInt(this, 10);

                return intValue;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return NaN;
            }
        }

        String.prototype.isInt = function () {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return false;
                }

                if (isNaN(this)) {
                    return false;
                }
                //TODO : remove all spaces
                //TODO : check for non numeric characters

                return true;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.isFloat = function () {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return false;
                }

                if (isNaN(this)) {
                    return false;
                }

                return true;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.toFloat = function () {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return NaN;
                }

                var floatValue = parseFloat(this);

                return floatValue;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return NaN;
            }
        }

        String.prototype.startsWith = function (prefix) {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return false;
                }

                if (isNotString(prefix)) {
                    return false;
                }

                if (prefix.length > this.length) {
                    return false;
                }

                var thisPrefix = this.substring(0, prefix.length);
                if (thisPrefix === prefix) {
                    return true;
                }

                return false;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.removePrefix = function (prefix) {
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return "";
                }

                if (isNotString(prefix)) {
                    return this;
                }

                if (prefix.isNullOrEmptyOrWhitespace()) {
                    return this;
                }

                if (prefix.length > this.length) {
                    return this;
                }

                if (this.startsWith(prefix)) {
                    var result = this.substring(prefix.length)
                    return result;
                }

                return this;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return this;
            }
        }

        String.prototype.contains = function (search) {
            try {
                if (this.isNullOrEmptyOrWhitespace()) {
                    return false;
                }

                if (isNotString(search)) {
                    return false;
                }

                if (search.length > this.length) {
                    return false;
                }

                var index = this.indexOf(search);
                if (index !== -1) {
                    return true;
                }

                return false;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.doesNotContain = function (search) {
            return this.contains(search) === false;
        }

        String.prototype.isInArray = function (inputArray) {
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return false;
                }

                if (isNotArray(inputArray)) {
                    return false;
                }

                var itemCount = inputArray.length;

                if (itemCount === 0) {
                    return false;
                }

                var thisValue = this.toString();

                for (var i = 0; i < itemCount; i++) {
                    if (inputArray[i] === thisValue) {
                        return true;
                    }

                }

                return false;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        String.prototype.isNotInArray = function (inputArray) {
            return this.isInArray(inputArray) === false;
        }

        String.prototype.replaceAll = function (stringToSearch, stringToReplace) {
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return "";
                }

                if (isNotString(stringToSearch)) {
                    return this;
                }

                if (isNotString(stringToReplace)) {
                    return this;
                }

                if (stringToSearch.isNullOrEmpty()) {
                    return this;
                }

                if (stringToSearch.length > this.length) {
                    return this;
                }

                var result = this;
                var length0 = result.length;
                for (var i = 0; i < 100; i++) {
                    if (result.doesNotContain(stringToSearch)) {
                        return result.toString();
                    }
                    result = result.replace(stringToSearch,stringToReplace);
                    var length = result.length;
                    if (length === length0) {
                        return result.toString();
                    }

                    length0 = length;
                }

                return this;

            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        String.prototype.leafName = function () {
            /// <signature>
            /// <summary>Get the leaf name on a string that represents an Url</summary>
            /// </signature>
            try {

                if (this.isNullOrEmptyOrWhitespace()) {
                    return "";
                }

                var inputArray = this.toArray("/");
                if (isNullOrUndefinedOrEmpty(inputArray)) {
                    return this;
                }

                var leafName = inputArray.pop();
                
                return leafName;

            } catch (e) {
                vpGrid.logException(e);
                return this;
            }
        }

        // end Javascript Types Extensions

        // jQuery extensions
        $.fn.extend({
            notFound: function () {
                try {
                    if (this === null) {
                        return true;
                    }

                    if (this === undefined) {
                        return true;
                    }

                    if (this.length === undefined) {
                        return true;
                    }

                    if (this.length === 0) {
                        return true;
                    }

                    return false;

                } catch (e) {
                    //TODO : log
                    vpGrid.logException(e);
                    return false;
                }
            }
        });

        $.fn.extend({
            found: function () {
                try {
                    if (this === null) {
                        return false;
                    }

                    if (this === undefined) {
                        return false;
                    }

                    if (this.length === undefined) {
                        return false;
                    }

                    if (this.length === 0) {
                        return false;
                    }

                    return true;

                } catch (e) {
                    //TODO : log
                    vpGrid.logException(e);
                    return false;
                }
            }
        });

        $.fn.extend({
            isCheckbox: function () {
                try {
                    if (this.notFound()) {
                        return false;
                    }

                    if (this.is(':checkbox')) {
                        return true;
                    }

                    return false;

                } catch (e) {
                    vpGrid.logException(e);
                    return false;
                }
            }
        });

        $.fn.extend({
            isNotCheckbox: function () {
                return this.isCheckbox === false;
            }
        });


        $.fn.extend({
            isUncheked: function () {
                try {
                    if (this.notFound()) {
                        return true;
                    }

                    if (this.is(':checked')) {
                        return true;
                    }

                    return false;

                } catch (e) {
                    vpGrid.logException(e);
                    return false;
                }
            }
        });
        // jQuery extensions
        $.fn.extend({
            toBooleanOrDefaultFromAttribute: function (attributeName, defaultValue) {
                try {
                    if (this === null) {
                        return defaultValue;
                    }

                    if (this === undefined) {
                        return defaultValue;
                    }

                    if (this.length === undefined) {
                        return defaultValue;
                    }

                    if (this.length === 0) {
                        return defaultValue;
                    }

                    if (isNotString(attributeName)) {
                        return defaultValue;
                    }

                    var attributeValue = this.attr(attributeName);
                    if (isNullOrUndefinedOrEmpty(attributeValue)) {
                        return defaultValue;
                    }

                    if (isNotString(attributeValue)) {
                        return defaultValue;
                    }

                    attributeValue = attributeValue.trim();

                    if (attributeValue.isNullOrEmptyOrWhitespace()) {
                        return defaultValue;
                    }

                    if (attributeValue === "true") {
                        return true;
                    }

                    return false;

                } catch (e) {
                    vpGrid.logException(e);
                    return defaultValue;
                }
            }
        });

        $.fn.extend({
            toIntOrDefaultFromAttribute: function (attributeName, defaultValue) {
                /// <signature>
                /// <summary>Get the attribute value as an integer</summary>
                /// <param name="attributeName" type="String">Name of the tag attribute</param>
                /// <param name="defaultValue" type="Number">Value to return if attribute is not found or is not in valid format</param>
                /// <returns type="Number">Returns the found attribute value or the defaultValue if attribute is not found or its value is not valid.</returns>
                /// </signature>
                try {
                    if (this === null) {
                        return defaultValue;
                    }

                    if (this === undefined) {
                        return defaultValue;
                    }

                    if (this.length === undefined) {
                        return defaultValue;
                    }

                    if (this.length === 0) {
                        return defaultValue;
                    }

                    if (isNotString(attributeName)) {
                        return defaultValue;
                    }

                    var attributeValue = this.attr(attributeName);
                    if (isNullOrUndefinedOrEmpty(attributeValue)) {
                        return defaultValue;
                    }

                    if (isNotString(attributeValue)) {
                        return defaultValue;
                    }

                    if (isNotNumber(attributeValue)) {
                        return defaultValue;
                    }

                    var intValue = attributeValue.toInt();

                    return intValue;

                } catch (e) {
                    vpGrid.logException(e);
                    return defaultValue;
                }
            }
        });

        $.fn.extend({
            toStringOrDefaultFromAttribute: function (attributeName, defaultValue) {
                try {
                    if (this === null) {
                        return defaultValue;
                    }

                    if (this === undefined) {
                        return defaultValue;
                    }

                    if (this.length === undefined) {
                        return defaultValue;
                    }

                    if (this.length === 0) {
                        return defaultValue;
                    }

                    if (isNotString(attributeName)) {
                        return defaultValue;
                    }

                    var attributeValue = this.attr(attributeName);
                    if (isNotString(attributeValue)) {
                        return defaultValue;
                    }

                    //trim the value;
                    attributeValue = attributeValue.trim();

                    if (attributeValue.isNullOrEmptyOrWhitespace()) {
                        return defaultValue;
                    }

                    return attributeValue;

                } catch (e) {
                    vpGrid.logException(e);
                    return defaultValue;
                }
            }
        });

        $.fn.extend({
            toEncodedHTML: function () {
                return this.each(function () {
                    try {

                        var jqElement = $(this);

                        // get the text inside this HTML tag
                        var rawText = jqElement.text();
                        var encodedText = rawText.toEncodedHTML();
                        jqElement.html(encodedText);

                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);

                    }
                });

            }
        });

        $.fn.extend({
            hideEx: function () {
                return this.each(function () {
                    try {
                        var jqElement = $(this);

                        // apply css class that will hide the element
                        jqElement.addClass("vp-grid-hidden");

                        if (jqElement.is("option")) {
                            jqElement.attr('disabled', 'true');

                            //TODO : backup current selected option in select tag

                            jqElement.removeAttr("selected");
                        }


                    } catch (e) {
                        vpGrid.logException(e);
                    }

                });
            }
        });

        $.fn.extend({
            showEx: function () {
                return this.each(function () {
                    try {
                        var jqElement = $(this);

                        // apply css class that will hide the element
                        jqElement.removeClass("vp-grid-hidden");

                        if (jqElement.is("option")) {
                            jqElement.removeAttr('disabled');
                            // TODO : restore current selected option in select tag
                        }


                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);

                    }

                });
            }
        });

        $.fn.extend({
            saveOriginalClassAttributeContent: function () {
                return this.each(function () {
                    try {
                        var jqElement = $(this);

                        if (jqElement.notFound()) {
                            vpGrid.logError({
                                functionName: "saveOriginalClassAttributeContent",
                                message: "Selector not found : " + jqElement.selector
                            });
                            return;
                        }

                        var originalContent = jqElement.toStringOrDefaultFromAttribute(
                                    vpGrid.HtmlDataTag.OriginalClassAttributeContent,
                                    "");

                        if (originalContent.isNotNullOrEmptyOrWhitespace()) {
                            // the save operation has already been done
                            return;
                        }

                        var classAttributeContent = jqElement.toStringOrDefaultFromAttribute("class", "");
                        if (classAttributeContent.isNullOrEmptyOrWhitespace()) {
                            classAttributeContent = "_"; // special hack in case jqElement has no class attribute
                        }

                        jqElement.attr(vpGrid.HtmlDataTag.OriginalClassAttributeContent, classAttributeContent)

                    } catch (e) {
                        vpGrid.logException(e);
                    }

                });
            }
        });

        $.fn.extend({
            restoreOriginalClassAttributeContent: function () {
                return this.each(function () {
                    try {
                        var jqElement = $(this);

                        if (jqElement.notFound()) {
                            vpGrid.logError({
                                functionName: "restoreOriginalClassAttributeContent",
                                message: "Selector not found : " + jqElement.selector
                            });
                            return;
                        }

                        var originalContent = jqElement.toStringOrDefaultFromAttribute(
                                    vpGrid.HtmlDataTag.OriginalClassAttributeContent,
                                    "");

                        jqElement.attr("class", originalContent);

                    } catch (e) {
                        vpGrid.logException(e);
                    }

                });
            }
        });

        $.fn.extend({
            openServerSideDialog: function (widthPercentage, heightPercentage) {
                /// <signature>
                /// <summary>Load an external url within an iframe as a modal pop-up. The iframe is wrapped in a jQuery-ui widget dialog modal window.</summary>
                /// <param name="widthPercentage" type="Number">width of the iframe expressed as a percentage of the current browser window width. The value must be between 0.1 and 0.9. If no value is supplied then default is 0.9</param>
                /// <param name="heightPercentage" type="Number">height of the iframe expressed as a percentage of the current browser window height. The value must be between 0.1 and 0.9. If no value is supplied then default is 0.9</param>
                /// </signature>
                try {
                    var jqElement = $(this);
                    if (jqElement.notFound()) {
                        vpGrid.logError({
                            message: "selector not found : " + jqElement.selector,
                            functionName: "openServerSideDialog"
                        });
                        return;
                    }

                    var windowSize = {
                        width: $(window).width(),
                        height: $(window).height()
                    }

                    //get the iframe url:
                    var iframeUrl = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.Url, "");

                    if (iframeUrl.isNullOrEmptyOrWhitespace()) {
                        var domElement = jqElement.html();
                        vpGrid.logError({
                            functionName: "openServerSideDialog",
                            message: "data attribute : " + vpGrid.HtmlDataTag.Url + " is not defined on element : \n" + domElement
                        });
                        return;
                    }

                    //get input parameters
                    var widthCoeff = 0.9;
                    if (isNumber(widthPercentage)) {
                        widthCoeff = widthPercentage;
                    }

                    var heightCoeff = 0.9;
                    if (isNumber(heightCoeff)) {
                        heightCoeff = heightPercentage;
                    }

                    var iframe = $('<iframe>', {
                        frameborder: 0,
                        scrolling: 'no',
                        width: windowSize.width * widthCoeff,
                        height: windowSize.height * heightCoeff,
                        src: iframeUrl
                    });

                    //change the dialog options
                    jqElement.dialog('option', {
                        width: "auto",
                        height: "auto"
                    })


                    //empty current content of the dialog
                    jqElement.html("");

                    iframe.appendTo(jqElement);
                    jqElement.dialog('open');

                } catch (e) {
                    vpGrid.logException(e);

                }
            }
        });

        $.fn.extend({
            openClientSideDialog: function (position, widthPercentage, heightPercentage) {
                /// <signature>
                /// <summary>Load an external url within an iframe as a modal pop-up. The iframe is wrapped in a jQuery-ui widget dialog modal window.</summary>
                /// <param name="widthPercentage" type="Number">width of the iframe expressed as a percentage of the current browser window width. The value must be between 0.1 and 0.9. If no value is supplied then default is 0.9</param>
                /// <param name="heightPercentage" type="Number">height of the iframe expressed as a percentage of the current browser window height. The value must be between 0.1 and 0.9. If no value is supplied then default is 0.9</param>
                /// </signature>
                try {
                    var jqElement = $(this);
                    if (jqElement.notFound()) {
                        vpGrid.logError({
                            message: "selector not found : " + jqElement.selector,
                            functionName: "openServerSideDialog"
                        });
                        return;
                    }

                    var windowSize = {
                        width: $(window).width(),
                        height: $(window).height()
                    }

                    //get input parameters
                    var widthCoeff = 0.9;
                    if (isNumber(widthPercentage)) {
                        widthCoeff = widthPercentage;
                    }

                    var heightCoeff = 0.9;
                    if (isNumber(heightCoeff)) {
                        heightCoeff = heightPercentage;
                    }

                    //change the dialog options
                    jqElement.dialog('option', {
                        width: "auto",
                        height: "auto"
                    })

                    if (isNotNullOrUndefinedOrEmpty(position)) {
                        jqElement.dialog('option', 'position', position)
                    }


                    var jqTitlebarContent = jqElement.closest('div.ui-dialog').find('div.ui-dialog-titlebar span.ui-dialog-title');
                    // check if there is custom content to inject in the title bar

                    if (jqTitlebarContent.notFound()) {
                        vpGrid.logError({
                            message: "Selector not found : '" + jqTitlebarContent.selector + "'",
                            functionName: "openClientSideDialog",
                            info: "The title cannot be modified on modal dialog element : \n" + outerHtml(jqElement[0])

                        });
                    }

                    var customTitleSelector = "div[role=" + vpGrid.HtmlRoleAttribute.Titlebar  + "]";
                    var customTitle = jqElement.find(customTitleSelector);

                    if (customTitle.found()) {
                        customTitle.detach();
                        jqTitlebarContent.html("");
                        customTitle.appendTo(jqTitlebarContent);
                        customTitle.removeClass("vp-grid-hidden");

                        // add special class to select element so that it is selectable
                        customTitle.find('select').addClass('ui-dialog-content');
                    }


                    jqElement.dialog('open');

                } catch (e) {
                    vpGrid.logException(e);

                }
            }
        });


        // end jQuery extensions

        //private extensions
        function isEmpty(input) {
            try {

                if (input === undefined) {
                    return true;
                }

                if (input === null) {
                    return true;
                }

                if (input.length === 0) {
                    return true;
                }

                //check for native types
                if (typeof (input) === "number") {
                    return false;
                }

                if (typeof (input) === "function") {
                    return false;
                }

                // check for empty literal object
                var propertyCount = 0;
                for (var i in input) {
                    propertyCount += 1;
                }
                if (propertyCount === 0) {
                    return true;
                }

                return false;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNullOrUndefined(input) {
            try {
                if (input === undefined) {
                    return true;
                }

                if (input === null) {
                    return true;
                }

                return false

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNullOrUndefinedOrEmpty(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return true;
                }

                return isEmpty(input);

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotNullOrUndefinedOrEmpty(input) {
            return isNullOrUndefinedOrEmpty(input) === false;
        }

        function isBoolean(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }

                if (typeof input === "boolean") {
                    return true;
                }

                return false;
            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotBoolean(input) {
            return isBoolean(input) === false;
        }

        function isString(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }

                if (typeof input === "string") {
                    return true;
                }

                return false;
            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotString(input) {
            return isString(input) === false;
        }

        function isFunction(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }

                if (typeof input === "function") {
                    return true;
                }

                return false;
            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotFunction(input) {
            return isFunction(input) === false;
        }

        function isNumber(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }
                if (isNaN(input)) {
                    return false;
                }
                if (typeof input === "number") {
                    return true;
                }

                if (isNotString(input)) {
                    return false;
                }

                // at this step we know that input is a string
                // try to convert to float
                var numberValue = input.toFloat();
                if (isNaN(numberValue)) {
                    return false;
                }

                return true;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotNumber(input) {
            return isNumber(input) === false;
        }

        function isInt(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }
                if (isNaN(input)) {
                    return false;
                }

                if (typeof input !== "number") {
                    return false;
                }

                var remainder = input % 1;
                if (remainder === 0) {
                    return true;
                }

                return false;

            } catch (e) {
                //TODO : log
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotInt(input) {
            return isInt(input) === false;
        }

        function isArray(input) {
            try {
                if (isNullOrUndefined(input)) {
                    return false;
                }

                if (typeof input === "array") {
                    return true;
                }

                if (input.push) {
                    return true;
                }

                return false;

            } catch (e) {
                vpGrid.logException(e);
                return false;
            }
        }

        function isNotArray(input) {
            return isArray(input) === false;
        }

        function outerHtml(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return "";
                }

                if (element.outerHTML) {
                    return element.outerHTML;
                }



                return "";

            } catch (e) {
                vpGrid.logException(e);
                return "";
            }
        }

        function extendOrMerge(obj1, obj2) {
            /// <signature>
            /// <summary>Extend obj1 with obj2 properties.</summary>
            /// <param name="obj1" type="Object">a litteral object to be extended</param>
            /// <param name="obj2" type="Object">feeder object literal</param>
            /// <returns type="Object">Returns obj1 augmented with properties coming from obj2. If obj1 and obj2 have identical propertiy, this property is converted to an array in obj1, and obj2 property value is pushed in this array.</returns>
            /// </signature>
            try {
                if (isNullOrUndefinedOrEmpty(obj2)) {
                    return obj1;
                }

                if (isNullOrUndefined(obj1)) {
                    var newObj = {};
                    $.extend(newObj, obj2);
                    return newObj;
                }

                if (isEmpty(obj1)) {
                    $.extend(obj1, obj2);
                    return obj1;
                }

                for (var i in obj2) {
                    var propertyName = i;
                    // check if this property exists in obj1
                    if (obj1[propertyName]) {
                        convertPropertyToArray(obj1, propertyName);
                        obj1[propertyName].push(obj2[propertyName]);
                        delete obj2[propertyName];
                    }
                }

                $.extend(obj1, obj2);
                return obj1;

            } catch (e) {
                vpGrid.logException(e);
                return obj1;
            }

        }

        function convertPropertyToArray(input, propertyName) {
            try {

                if (isNullOrUndefinedOrEmpty(input)) {
                    return;
                }

                if (isNotString(propertyName)) {
                    return;
                }

                if (isNullOrUndefined(input[propertyName])) {
                    return;
                }

                // now we are sure that the property named propertyName exists on input object
                var obj = input[propertyName];
                if (isArray(obj)) {
                    // the conversion is already done
                    return input;
                }

                input[propertyName] = [];
                input[propertyName].push(obj);

                return input;

            } catch (e) {
                vpGrid.logException(e);
                return;
            }
        }

        function tryFindFunctionByName(input) {
            try {
                if (isNullOrUndefinedOrEmpty(input)) {
                    return null;
                }

                if (isNotString(input)) {
                    return null;
                }

                // check if the function exist in the root object
                var foundFunction = window[input];
                if (isFunction(foundFunction)) {
                    return foundFunction;
                }

                // function is defined in a namespace
                var namespaces = input.split(".");
                if (isNullOrUndefinedOrEmpty(namespaces)) {
                    return null;
                }

                var functionName = namespaces[namespaces.length - 1];
                var parentObject = window;
                for (var i = 0; i < namespaces.length - 1; i++) {
                    var namespace = namespaces[i];
                    parentObject = parentObject[namespace];
                }

                foundFunction = parentObject[functionName];
                if (isFunction(foundFunction)) {
                    return foundFunction;
                }

                vpGrid.logError({
                    functionName: "tryFindFunctionByName",
                    message: "Cannot find function named : '" + input + "'"
                });
                return null;

            } catch (e) {
                vpGrid.logException(e);
                return null;
            }
        }

        function executeAsync(f, delay, asyncKey) {
            try {
                if (isNotFunction(f)) {
                    return;
                }

                var defaultDelay = 0;
                if (isNumber(delay)) {
                    defaultDelay = delay;
                }

                if (isNullOrUndefinedOrEmpty(asyncKey)) {
                    setTimeout(f, defaultDelay);
                    return;
                }

                //TODO : implement debounce


            } catch (e) {
                vpGrid.logException(e);
                setTimeout(f, 0);
            }
        }
        //end private extensions

        // jQuery-UI extensions
        function bindWidgetOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                // take the name of the widget from the data tag
                var widgetType = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.UIWidget, "");

                //deserialize the configuration options object from the data tag
                var dataTag = vpGrid.HtmlDataTag.UIWidgetOptions.removePrefix("data-");
                var widgetOptions = jqElement.data(dataTag);

                if (widgetType === "datepicker") {
                    // bind datepicker widget
                    //merge widget Options with vpGrid datepicker Options
                    var mergedOptions = $.extend({}, vpGridOptions.datepickerOptions, widgetOptions);

                    jqElement.datepicker(mergedOptions);
                    return;
                } // end if

                if (widgetType === "button") {
                    // bind button widget
                    jqElement.button(widgetOptions);
                    return;
                } // end if

                if (widgetType === "dialog") {
                    // bind dialog widget
                    // bind internal event handler onDialogClosed
                    //TODO : check if an handler is already bound
                    var source = element;
                    widgetOptions.close = function (event, ui) {
                        try {
                            var eventArgs = {};
                            // raise event so that listeners can do extra process
                            onModalDialogClosed(source, eventArgs);

                        } catch (e) {
                            vpGrid.logException(e);
                        }
                    };

                    jqElement.dialog(widgetOptions);
                    return;
                } // end if

                if (widgetType === "autocomplete") {
                    //bind autocomplete widget
                    bindAutoCompleteWidgetOnElement(element);
                    return;
                } // end if

                if (widgetType === "accordion") {
                    //bind accordion widget
                    jqElement.accordion(widgetOptions);
                    return;
                } // end if

                vpGrid.logError({
                    functionName: "bindWidgetOnElement",
                    message: "widget binding is not implemented for widget : '" + widgetType + "'  in vpGrid.",
                    info: "This widget is not binded on element : \n" + outerHtml(element)
                });

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindAutoCompleteWidgetOnElement(element) {
            try {

                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                var widgetDataSource = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSource, "")
                var widgetDataSourceType = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSourceType, vpGrid.HtmlDataTag.DefaultValue.DataSourceType);

                if (widgetDataSource.isNullOrEmptyOrWhitespace()) {
                    //TODO : log
                    vpGrid.logError({
                        message: "data attribute : " + vpGrid.HtmlDataTag.DataSource + " is not defined on element \n: " + outerHtml(element)
                    });
                    return;
                }

                if (widgetDataSourceType === "web") {
                    bindAutoCompleteWidgetFromWebOnElement(element, widgetDataSource);
                    return;
                }

                //TODO : log
                vpGrid.logError({
                    functionName: "bindAutoCompleteWidgetOnElement",
                    message: "data attribute : " + vpGrid.HtmlDataTag.DataSourceType + " on element \n: " + outerHtml(element) + "\n has a value that is not implemented in vpGrid."
                });

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindAutoCompleteWidgetFromWebOnElement(element, dataSourceUrl) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                var ajaxOptions = {
                    cache: false,
                    type: "GET",
                    url: dataSourceUrl,
                    dataType: "json",
                    element: element,
                    success: function (response) {
                        bindAutoCompleteWidgetFromWebResponseOnElement(response, element);
                    }

                }

                $.extend(ajaxOptions, vpGridAjaxOptions);

                //TODO : raise event to enable listerners inspect and/or modify ajaxOptions

                $.ajax(ajaxOptions);


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindAutoCompleteWidgetFromWebResponseOnElement(response, element) {
            try {

                //TODO : raise event to enable listerners inspect and/or modify response
                if (isNullOrUndefinedOrEmpty(response)) {
                    //TODO : log
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (response.done === undefined) {
                    //TODO : log
                    return;
                }

                if (response.done === false) {
                    var details = "Ajax Request error on fetching data for autocomplete";
                    logServerError(response, details, element);
                    return;
                }

                var data = response.data;
                bindAutoCompleteWidgetWithDataOnElement(element, data);

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindAutoCompleteWidgetWithDataOnElement(element, data) {
            try {

                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                //deserialize the configuration options object from the data tag
                var dataTag = vpGrid.HtmlDataTag.UIWidgetOptions.removePrefix("data-");
                var widgetOptions = jqElement.data(dataTag);
                if (isNullOrUndefined(widgetOptions)) {
                    widgetOptions = {};
                }

                widgetOptions.source = data;

                // set default minLength value if not already setup
                if (isNullOrUndefinedOrEmpty(widgetOptions.minLength)) {
                    widgetOptions.minLength = 0;
                }

                jqElement.autocomplete(widgetOptions);
                jqElement.bind('focus', function () {
                    try {
                        var jqCurrent = $(this);
                        executeAsync(function () {
                            try {
                                var currentValue = jqCurrent.val();
                                if (isNullOrUndefinedOrEmpty(currentValue)) {
                                    currentValue = "";
                                }

                                jqCurrent.autocomplete("search", currentValue);

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                });

                jqElement.bind('click', function () {
                    try {
                        var jqCurrent = $(this);
                        executeAsync(function () {
                            try {
                                jqCurrent.autocomplete("search", "");

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                });


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function destroyWidgetOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (jqElement.datepicker) {
                    //unbind the close event
                    jqElement.datepicker("option", "onClose", function () { });
                    jqElement.datepicker('hide');
                    jqElement.datepicker("destroy");
                    return;
                }

                if (jqElement.autocomplete) {
                    jqElement.autocomplete("close");
                    jqElement.autocomplete("destroy");
                    return;
                }

                vpGrid.logError({
                    functionName: "destroyWidgetOnElement",
                    message: "Widget destroy is not implemented on element : " + outerHtml(element)
                });

                //TODO : implement destroy of other possible widgets

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function hideWidgetOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (jqElement.datepicker) {
                    jqElement.datepicker('hide');
                    return;
                }

                if (jqElement.autocomplete) {
                    jqElement.autocomplete("close");
                    return;
                }

                vpGrid.logError({
                    message: "Widget hide is not implemented on element : \n" + outerHtml(element)
                });

                //TODO : implement hide of other possible widgets

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        // end jQuery-UI extensions


        //UI extensions
        function applyDisabledStateOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                jqElement.attr('disabled', true).addClass("ui-state-disabled");


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function applyActiveHeaderOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                jqElement.mouseenter(
                   function () {
                       $(this).find('[role=vp-grid-passive-header]').addClass('vp-grid-hidden');
                       $(this).find('[role=vp-grid-active-header]').removeClass('vp-grid-hidden');
                       $(this).saveOriginalClassAttributeContent();
                       $(this).addClass($(this).attr(vpGrid.HtmlDataTag.CssToAdd));
                       //bind event handlers
                       bindHandlersOnElement(this);
                   }
                   );

                jqElement.mouseleave(
                    function () {
                        $(this).find('[role=vp-grid-passive-header]').removeClass('vp-grid-hidden');
                        $(this).find('[role=vp-grid-active-header]').addClass('vp-grid-hidden');
                        $(this).restoreOriginalClassAttributeContent();


                    }
                    );

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindDataSourceOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (jqElement.is("select")) {
                    //bind data source on select
                    bindDataSourceOnSelectElement(element);
                    return;
                } // end if

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindDataSourceOnSelectElement(element) {
            try {

                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                var selectDataSource = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSource, "")
                var selectDataSourceType = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSourceType, vpGrid.HtmlDataTag.DefaultValue.DataSourceType);

                if (selectDataSource.isNullOrEmptyOrWhitespace()) {
                    //TODO : log
                    vpGrid.logError({
                        message: "data attribute : " + vpGrid.HtmlDataTag.DataSource + " is not defined on element : \n" + outerHtml(element)
                    });
                    return;
                }

                if (selectDataSourceType === "web") {
                    bindDataSourceFromWebOnSelectElement(element, selectDataSource);
                    return;
                }

                //TODO : log
                vpGrid.logError({
                    message: "data attribute : " + vpGrid.HtmlDataTag.DataSourceType + " on element : \n" + outerHtml(element) + "\n has a value that is not implemented in vpGrid."
                });



            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindDataSourceFromWebOnSelectElement(element, dataSourceUrl) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                var ajaxOptions = {
                    cache: false,
                    type: "GET",
                    url: dataSourceUrl,
                    dataType: "json",
                    element: element,
                    success: function (response) {
                        bindDataSourceFromWebResponseOnSelectElement(response, element);
                    }

                }

                $.extend(ajaxOptions, vpGridAjaxOptions);

                //TODO : raise event to enable listerners inspect and/or modify ajaxOptions

                $.ajax(ajaxOptions);


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindDataSourceFromWebResponseOnSelectElement(response, element) {
            try {

                //TODO : raise event to enable listerners inspect and/or modify response
                if (isNullOrUndefinedOrEmpty(response)) {
                    //TODO : log
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (response.done === undefined) {
                    //TODO : log
                    return;
                }

                if (response.done === false) {
                    var details = "Ajax Request error on fetching data for select element";
                    logServerError(response, details, element);
                    return;
                }

                var data = response.data;
                bindSelectWithDataOnElement(element, data);

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function bindSelectWithDataOnElement(element, data) {
            try {

                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                //TODO : check that data param is an array
                //TODO ; check that items in the array are of the form { key:xx, value:yy [,selected: true]}

                //empty the select element
                jqElement.find('option').remove();

                var length = data.length;

                for (var i = 0; i < length; i++) {
                    var item = data[i];

                    var optionValue = item.key;
                    var optionText = item.value;
                    var isSelected = false;
                    if (item.selected) {
                        isSelected = true;
                    }

                    var optionElement = $("<option></option>").attr("value", optionValue).text(optionText);
                    if (isSelected) {
                        optionElement.attr("selected", "true");
                    }
                    jqElement.append(optionElement);

                }


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function fillContentPlaceholderOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                var dataSource = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSource, "")
                var dataSourceType = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSourceType, vpGrid.HtmlDataTag.DefaultValue.DataSourceType);

                if (dataSource.isNullOrEmptyOrWhitespace()) {
                    //TODO : log
                    vpGrid.logError({
                        message: "data attribute : " + vpGrid.HtmlDataTag.DataSource + " is not defined on element : \n" + outerHtml(element)
                    });
                    return;
                }

                if (dataSourceType === "handler") {
                    fillContentPlaceholderFromHandlerOnElement(element, dataSource);
                    return;
                }

                //TODO : log
                vpGrid.logError({
                    message: "data attribute : " + vpGrid.HtmlDataTag.DataSourceType + " on element : \n" + outerHtml(element) + "\n has a value that is not implemented in vpGrid."
                });
            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function fillContentPlaceholderFromHandlerOnElement(element, handlerName) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }
                
                var handler = tryFindFunctionByName(handlerName)

                if (isNotFunction(handler)) {
                    var domHTML = outerHtml(element);
                    vpGrid.logError({
                        functionName : "fillContentPlaceholderFromHandlerOnElement",
                        message: "Cannot find handler named : '" + handlerName + "' defined on element: \n" + domHTML
                    });
                    return;
                }

                // call handler to get html content
                var newContent = handler();
                if (isNullOrUndefinedOrEmpty(newContent)) {
                    newContent = "";
                }

                jqElement.html(newContent);

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        // end UI extensions

        //UI event handlers auto binding
        function bindHandlersOnElement(element) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                // check if handlers have already been registered 
                var handlersRegistered = jqElement.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.HandlersRegistered, false);
                if (handlersRegistered) {
                    return;
                }

                //deserialize the configuration options object from the data tag
                var dataTag = vpGrid.HtmlDataTag.Handlers.removePrefix("data-");
                var handlers = jqElement.data(dataTag);


                //handlers is a literal object that is like :
                // {"click" : { "handler" : "MyCompany.MyHandler", "async" : true, "asyncKey" : "myHandlerKey"}}

                if (isNullOrUndefinedOrEmpty(handlers)) {
                    //TODO : log
                    return;
                }

                for (var i in handlers) {
                    var eventName = i;
                    var handlerOptions = handlers[i];
                    bindHandlerOnElement(element, eventName, handlerOptions);
                }

                // flag that events have been binded
                jqElement.attr(vpGrid.HtmlDataTag.HandlersRegistered, "true");


            } catch (e) {
                vpGrid.logException(e);
            }
            finally {

            }
        }

        function bindHandlerOnElement(element, eventName, handlerOptions) {
            try {
                if (isNullOrUndefined(element)) {
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    return;
                }

                if (isNullOrUndefinedOrEmpty(eventName)) {
                    //TODO : log
                    return;
                }

                if (isNotString(eventName)) {
                    //TODO : log

                    return;
                }

                if (isNullOrUndefinedOrEmpty(handlerOptions)) {
                    //TODO : log
                    return;
                }

                var handlerName = handlerOptions.handler;
                var handler = tryFindFunctionByName(handlerName)

                if (isNotFunction(handler)) {
                    var domHTML = outerHtml(element);
                    vpGrid.logError({
                        message: "Cannot find handler named : '" + handlerName + "' for " + eventName + " event defined on element: \n" + domHTML
                    });
                    return;
                }

                if (isNullOrUndefined(handlerOptions.async)) {
                    handlerOptions.async = false;
                }

                if (eventName === "click" && handlerOptions.async === false) {
                    jqElement.click(handler);
                    return;
                }

                if (eventName === "change" && handlerOptions.async === false) {
                    jqElement.change(handler);
                    return;
                }

                if (eventName === "click" && handlerOptions.async === true) {
                    jqElement.click(function (event) {
                        //TODO : get asyncKey from data attribute
                        //TODO : get prevent default behavior from data attribute

                        if (event) {
                            if (event.preventDefault) {
                                event.preventDefault();
                            }
                        }

                        executeAsync(function () {
                            handler(event);
                        });
                    });


                    return;
                }

                if (eventName === "change" && handlerOptions.async === true) {
                    jqElement.change(function (event) {
                        //TODO : get asyncKey from data attribute
                        //TODO : get prevent default behavior from data attribute

                        if (event) {
                            if (event.preventDefault) {
                                event.preventDefault();
                            }
                        }

                        executeAsync(function () {
                            handler(event);
                        });
                    });


                    return;
                }

                //TODO : log error event handler not implemented

            } catch (e) {
                vpGrid.logException(e);
            }
            finally {

            }
        }
        //end UI event handlers auto binding


        //vpGrid Events
        //Public Events enumeration
        vpGrid.Event = vpGrid.Event || {};
        vpGrid.Event.onSecurityTrimming = "onSecurityTrimming";
        vpGrid.Event.onTimelineCreated = "onTimelineCreated";
        vpGrid.Event.onBehaviorsRegistering = "onBehaviorsRegistering";
        vpGrid.Event.onBehaviorsRegistered = "onBehaviorsRegistered";
        vpGrid.Event.onTimelineExecuting = "onTimelineExecuting";

        //This event is raised when a cell is in edit mode and needs to be rendered as a select statement <select>...</select>
        //Event handler must have the following signature : function(source, eventArgs)
        //the eventArgs parameter has the following signature : 
        //      eventArgs = {
        //        cellName: cellname,
        //        cellValueBeforeEditing: value,
        //        rowData: data,
        //        selectOptions : []
        //      };
        // The event handler should fill the eventArgs.selectOptions with literal objects of type : { key : xxx, value : yyy} (a collection of key/value pairs)
        // if there is no listener or if listener(s) does nothing, the selectOptions array will be be automatically filled with all distinct values of the column.
        vpGrid.Event.onSelectCellRendering = "onSelectCellRendering";

        //This event is raised when a cell is in edit mode and needs to be rendered as an input statement <input>...</input> with autocomplete bound to it.
        //Event handler must have the following signature : function(source, eventArgs)
        //the eventArgs parameter has the following signature : 
        //      eventArgs = {
        //        cellName: cellname,
        //        cellValue: value,
        //        options : {},  -> options parameter of autocomplete jquery-ui
        //        data: [],
        //      };
        // The event handler should fill the data array with strings. This array will be bound to autocomplete.
        // if there is no listener or if listener(s) does nothing, the data array will be be automatically filled with all distinct values of the column.
        vpGrid.Event.onAutocompleteCellRendering = "onAutocompleteCellRendering";
        

        //This event is raised when a cell is in display mode and needs to be rendered as an image statement <img>...</img>
        vpGrid.Event.onImageCellRendering = "onImageCellRendering";

        //This event is raised when a cell is in display mode and needs to be rendered as a link statement <a>...</a>
        vpGrid.Event.onLinkCellRendering = "onLinkCellRendering";


        //This event is raised when a cell is in edit mode and needs to be validated through custom validation
        //Event handler must have the following signature : function(source, eventArgs)
        //the eventArgs parameter has the following signature : 
        //      eventArgs = {
        //        cellName: cellname,
        //        cellValue: value,
        //        rowData: data,
        //        isValid : true,
        //        errorMessage : 'xxx'
        //      };
        // The event handler should set the eventArgs.isValid property to false and the eventArgs.errorMessage property if custom validation fails.
        vpGrid.Event.onCellValidating = "onCellValidating";



        vpGrid.Event.onModalDialogClosing = "onModalDialogClosing";

        ///This Event is raised when a cell is in edit mode and the underlying data is about to be updated.
        vpGrid.Event.onCellUpdating = "onCellUpdating";

        //Event raised when a modal dialog is closed. 
        //Event listener must have the signature function(source, eventArgs){}
        vpGrid.Event.onModalDialogClosed = "onModalDialogClosed";

        //Private Events Enumeration
        var PrivateEvent = {};
        PrivateEvent.onBehaviorsRegistering = "private.onBehaviorsRegistering";

        //EventHandlers layer
        var EventHandlers = (function () {
            var eventHandlers = {};
            try {
                var listeners = {};
                //private methods on EventHandlers layer
                function eventHasListeners(eventName) {
                    try {
                        if (isNotString(eventName)) {
                            return false;
                        }

                        if (eventName.isNullOrEmptyOrWhitespace()) {
                            return false;
                        }

                        if (isNullOrUndefinedOrEmpty(listeners)) {
                            return false;
                        }

                        var eventNameListeners = listeners[eventName];

                        if (isNullOrUndefinedOrEmpty(eventNameListeners)) {
                            return false;
                        }

                        return true;

                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);
                        return false;
                    }
                }

                function eventHasNoListener(eventName) {
                    return eventHasListeners(eventName) === false;
                }

                function eventHandlerIsRegistered(eventName, handler) {
                    try {
                        if (eventHasNoListener(eventName)) {
                            return false;
                        }

                        if (isNullOrUndefined(handler)) {
                            return false;
                        }

                        if (isNotFunction(handler)) {
                            return false;
                        }

                        // get all listeners for this event
                        var eventListeners = listeners[eventName];
                        var listernersCount = eventListeners.length;

                        for (var i = 0; i < listernersCount; i++) {
                            if (eventListeners[i] === handler) {
                                return true;
                            }
                        }

                        return false;

                    } catch (e) {
                        vpGrid.logException(e);
                        return false;
                    }
                }

                //public methods on this layer
                eventHandlers.registerEventHandler = function (eventName, handler) {
                    /// <signature>
                    /// <summary>Register your event handler to one of the vpGrid events</summary>
                    /// <param name="eventName" type="String">Name of the event you want to listen to. Possible names are defined in the the vpGrid enumeration vpGrid.Event</param>
                    /// <param name="handler" type="Function">Function that will be called when raising the event. This function must have the signature function(source, eventArgs){}, where source is the object that is at the origin of the event, and eventArgs is a placeholder object for informations needed by the listener.</param>
                    /// </signature>
                    try {
                        if (isNotFunction(handler)) {
                            //TODO : log
                            return;
                        }

                        // check if event handler has already been registered
                        if (eventHandlerIsRegistered(eventName, handler)) {
                            return;
                        }
                        var eventListeners = listeners[eventName];
                        if (isNullOrUndefinedOrEmpty(eventListeners)) {
                            listeners[eventName] = [];
                        }

                        listeners[eventName].push(handler);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                eventHandlers.raiseEvent = function (eventName, eventSource, eventArgs) {
                    try {
                        if (eventHasNoListener(eventName)) {
                            return;
                        }

                        var eventListeners = listeners[eventName];
                        var listernersCount = eventListeners.length;

                        for (var i = 0; i < listernersCount; i++) {
                            handler = eventListeners[i];
                            try {
                                // execute handler
                                handler(eventSource, eventArgs);

                                //TODO : check if cancel has been setup in eventArgs
                            } catch (e) {
                                //TODO : log
                                vpGrid.logException(e);
                            }
                        }

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }


            } catch (e) {
                //error in  EventHandlers layer
                vpGrid.logException(e);
            }
            finally {
                return eventHandlers;
            }

        })();
        //end EventHandlers layer
        // public interface on Event Handlers
        vpGrid.registerEventHandler = function (eventName, handler) {
            try {
                EventHandlers.registerEventHandler(eventName, handler);
            } catch (e) {
                vpGrid.logException(e);
            }

        }

        //Behaviors layer
        var Behaviors = (function () {
            var behaviors = {};
            try {
                // Sequence Enumeration
                behaviors.Sequence = {};
                behaviors.Sequence.SequenceInit = "SequenceInit";
                behaviors.Sequence.GridPreInit = "GridPreInit";
                behaviors.Sequence.GridInit = "GridInit";
                behaviors.Sequence.GridLoading = "GridLoading";
                behaviors.Sequence.GridLoaded = "GridLoaded";
                behaviors.Sequence.SequenceEnd = "SequenceEnd";
                behaviors.Sequence.SequenceUndefined = "SequenceUndefined";

                var defaultTimeline = [
                    behaviors.Sequence.SequenceInit,
                    behaviors.Sequence.GridPreInit,
                    behaviors.Sequence.GridInit,
                    behaviors.Sequence.GridLoading,
                    behaviors.Sequence.GridLoaded,
                    behaviors.Sequence.SequenceEnd
                ];

                function createTimeline() {
                    try {
                        // create a copy of the default timeline array
                        var timeline = defaultTimeline.slice(0);

                        return timeline;

                    } catch (e) {
                        vpGrid.logException(e);
                        return [];
                    }

                }

                //registered behaviors
                var timelineBehaviors = {};

                //private methods on Behaviors layer
                function timelineSequenceHasBehaviors(sequenceName) {
                    try {
                        if (isNotString(sequenceName)) {
                            return false;
                        }

                        if (sequenceName.isNullOrEmptyOrWhitespace()) {
                            return false;
                        }

                        if (isNullOrUndefinedOrEmpty(timelineBehaviors)) {
                            return false;
                        }

                        var sequenceNameBehaviors = timelineBehaviors[sequenceName];

                        if (isNullOrUndefinedOrEmpty(sequenceNameBehaviors)) {
                            return false;
                        }

                        return true;

                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);
                        return false;
                    }
                }

                function timelineSequenceHasNoBehavior(sequenceName) {
                    return timelineSequenceHasBehaviors(sequenceName) === false;
                }

                function behaviorIsRegistered(sequenceName, handlerInfo) {
                    try {
                        if (timelineSequenceHasNoBehavior(sequenceName)) {
                            return false;
                        }

                        if (isNullOrUndefinedOrEmpty(handlerInfo)) {
                            return false;
                        }

                        if (isNotFunction(handlerInfo.execute)) {
                            //TODO : log
                            return false;
                        }

                        // get all behaviors for this sequence
                        var sequenceNameBehaviors = timelineBehaviors[sequenceName];
                        var behaviorsCount = sequenceNameBehaviors.length;

                        for (var i = 0; i < behaviorsCount; i++) {
                            if (sequenceNameBehaviors[i].execute === handlerInfo.execute) {
                                return true;
                            }
                        }

                        return false;

                    } catch (e) {
                        vpGrid.logException(e);
                        return false;
                    }
                }

                function registerBehavior(sequenceName, handlerInfo) {
                    /// <signature>
                    /// <summary>Register a behavior to be executed when executing the timeline sequence</summary>
                    /// <param name="sequenceName" type="String">the name of a sequence inside the execution timeline.</param>
                    /// <param name="handlerInfo" type="Object">A literal object of form {execute: function, isAsync : boolean}. 
                    ///     execute property is the function that will be called (the behavior) when executing the timeline's sequence. 
                    ///     isAsync property signals if the behavior executes itself asynchronous.
                    /// </param>
                    /// </signature>
                    try {
                        if (isNotString(sequenceName)) {
                            vpGrid.logError({
                                message: "Cannot register behavior : the sequence name is not a string"
                            });
                            return;
                        }
                        if (isNullOrUndefinedOrEmpty(handlerInfo)) {
                            vpGrid.logError({
                                message: "Cannot register behavior on sequence : '" + sequenceName + "'. Behavior Info is null or empty."
                            });
                            return;
                        }

                        if (isNotFunction(handlerInfo.execute)) {
                            vpGrid.logError({
                                message: "Cannot register behavior : behavior callback is not a function."
                            });
                            return;
                        }

                        // check if behavior has already been registered
                        if (behaviorIsRegistered(sequenceName, handlerInfo)) {
                            return;
                        }

                        //set isAsync property if not set
                        if (isNotBoolean(handlerInfo.isAsync)) {
                            handlerInfo.isAsync = false;
                        }

                        var sequenceNameBehaviors = timelineBehaviors[sequenceName];
                        // check first time registration
                        if (isNullOrUndefinedOrEmpty(sequenceNameBehaviors)) {
                            timelineBehaviors[sequenceName] = [];
                        }

                        timelineBehaviors[sequenceName].push(handlerInfo);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function onTimelineCreated(eventArgs) {
                    try {

                        EventHandlers.raiseEvent(vpGrid.Event.onTimelineCreated, vpGrid, eventArgs);

                        //TODO : check eventArgs corruption
                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "onTimelineCreated",
                                message: "eventArgs object corrupted by listener"
                            });
                            return;

                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.timeline)) {
                            vpGrid.logError({
                                functionName: "onTimelineCreated",
                                message: "eventArgs.timeline object corrupted by listener"
                            });
                            return;

                        }

                        //TODO : check that timeline is an array and contains only strings





                    } catch (e) {
                        vpGrid.logException(e);
                    }

                }

                function onBehaviorsRegistration(timeline, eventName) {
                    try {
                        if (isNullOrUndefinedOrEmpty(timeline)) {
                            //TODO : log
                            return;
                        }
                        if (isNotString(eventName)) {
                            //TODO : log
                            return;
                        }

                        var timelineCount = timeline.length;
                        for (var i = 0; i < timelineCount; i++) {
                            var _sequenceName = timeline[i];
                            var eventArgs = {
                                sequenceName: _sequenceName,
                                behaviors: []
                            };
                            EventHandlers.raiseEvent(eventName, vpGrid, eventArgs);

                            if (isNullOrUndefinedOrEmpty(eventArgs.behaviors)) {
                                // no listeners or listeners that do nothing
                                continue;
                            }

                            // register behaviors given by listeners
                            var behaviorsCount = eventArgs.behaviors.length;
                            for (var j = 0; j < behaviorsCount; j++) {
                                registerBehavior(_sequenceName, eventArgs.behaviors[j]);
                            }
                        }

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function createExecutionSteps(timeline) {
                    try {
                        var steps = [];
                        if (isNullOrUndefinedOrEmpty(timeline)) {
                            vpGrid.logError({
                                functionName: "createExecutionSteps",
                                message: "timeline is empty : none of the behaviors will execute."
                            });
                            return;
                        }

                        var timelineCount = timeline.length;
                        for (var i = 0; i < timelineCount; i++) {
                            var _sequenceName = timeline[i];

                            // get behaviors registers for this sequence name
                            if (timelineSequenceHasNoBehavior(_sequenceName)) {
                                continue;
                            }
                            var _behaviors = timelineBehaviors[_sequenceName];
                            var behaviorsCount = _behaviors.length;
                            for (var j = 0; j < behaviorsCount; j++) {
                                var executionStep = {
                                    sequenceName: _sequenceName,
                                    behavior: _behaviors[j] // this property is a literal object like {isAsync : true|false, execute : function}
                                };
                                steps.push(executionStep);
                            } // end for

                        } // for (var i = 0; i < timelineCount; i++)

                        return steps;

                    } catch (e) {
                        vpGrid.logException(e);
                        return [];
                    }
                }

                function onTimelineExecuting(eventArgs) {
                    try {

                        //enable listeners to inspect and/or modify all execution steps
                        EventHandlers.raiseEvent(vpGrid.Event.onTimelineExecuting, vpGrid, eventArgs);

                        //TODO : check any corruption in eventArgs
                        //TODO : check any change in executionSteps

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function executeBehaviors(executionSteps) {
                    try {
                        if (isNullOrUndefinedOrEmpty(executionSteps)) {
                            //TODO : log
                            return;
                        }

                        function processNext() {
                            /// <signature>
                            /// <summary>Core execution engine of the timeline.
                            /// This engine is called multiple times until there is no behavior to execute
                            ///</summary>
                            /// </signature>
                            if (isNullOrUndefinedOrEmpty(executionSteps)) {
                                // there is no more step to execute
                                return;
                            }

                            var executionStep = executionSteps.shift();
                            //TODO : log execution step
                            // execute behavior
                            try {
                                // call the behavior and give him the callback method to call when its async processing is finished.
                                executionStep.behavior.execute(executionStep.sequenceName, processNext);
                                if (executionStep.behavior.isAsync) {
                                    // processNext will be called by the behavior itself
                                    return;
                                }

                                // let the UI update itself if needed
                                setTimeout(processNext, 0);

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        } // end function processNext()

                        //start execution engine
                        processNext();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                //public methods on this layer
                //behaviors.registerBehavior = registerBehavior;

                behaviors.timelineSequenceExists = function (sequenceName) {
                    try {
                        if (isNotString(sequenceName)) {
                            return false;
                        }

                        if (sequenceName.isInArray(behaviors.currentTimeline)) {
                            return true;
                        }

                        return false;

                    } catch (e) {
                        vpGrid.logException(e);
                        return false;
                    }
                }

                behaviors.timelineSequenceDoesNotExist = function (sequenceName) {
                    return behaviors.timelineSequenceExists(sequenceName) === false;
                }

                behaviors.getTimelineSequenceOnElement = function (element, defaultTimelineSequence, behaviorName) {
                    try {
                        var jqElement = $(element);

                        if (jqElement.notFound()) {
                            //TODO : log
                            return behaviors.Sequence.SequenceUndefined;
                        }

                        if (isNotString(behaviorName)) {
                            behaviorName = "undefined behavior";
                        }

                        if (isNotString(defaultTimelineSequence)) {
                            defaultTimelineSequence = behaviors.Sequence.GridInit;
                        }

                        var dataSequenceName = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.TimelineSequence, "");
                        if (dataSequenceName.isNullOrEmptyOrWhitespace()) {
                            //TODO : log warning instead of error
                            vpGrid.logError({
                                message: "data attribute : " + vpGrid.HtmlDataTag.TimelineSequence + " is not defined on element : \n" + outerHtml(element),
                                info: "the following default value is applied to this data attribute: " + defaultTimelineSequence
                            });
                            dataSequenceName = defaultTimelineSequence;
                        }

                        if (behaviors.timelineSequenceDoesNotExist(dataSequenceName)) {
                            vpGrid.logError({
                                message: "data attribute value : " + vpGrid.HtmlDataTag.TimelineSequence + "='" + dataSequenceName + "' is not defined in the timeline. Target element : \n" + outerHtml(element),
                                info: "This value must be in [" + behaviors.currentTimeline.toString() + "]. Behavior " + behaviorName + " is not applied to this element."
                            });
                        }

                        return dataSequenceName;

                    } catch (e) {
                        vpGrid.logException(e);
                        return behaviors.Sequence.SequenceUndefined;
                    }
                }

                behaviors.executeTimeline = function () {
                    try {
                        var timeline = createTimeline();

                        //raise event to enable listeners to inspect and/or modify the timeline
                        var eventArgs = {
                            timeline: timeline
                        };
                        onTimelineCreated(eventArgs);

                        //make this timeline public
                        behaviors.currentTimeline = eventArgs.timeline;

                        //raise event onBehaviorsRegistering -> extension point to inject behaviors before internal behaviors
                        onBehaviorsRegistration(eventArgs.timeline, vpGrid.Event.onBehaviorsRegistering);

                        //inject internal behaviors
                        onBehaviorsRegistration(eventArgs.timeline, PrivateEvent.onBehaviorsRegistering);

                        //raise event onBehaviorsRegistered -> extension point to inject behaviors after internal behaviors
                        onBehaviorsRegistration(eventArgs.timeline, vpGrid.Event.onBehaviorsRegistered);

                        // get all execution steps
                        // an execution step is a behavior that will be executed at a specific point in the timeline
                        var executionSteps = createExecutionSteps(eventArgs.timeline);

                        //raise event onTimelineExecuting -> extension point to examine all execution steps in the timeline before they start executing
                        var eventArgs2 = {
                            executionSteps: executionSteps
                        };
                        onTimelineExecuting(eventArgs2);
                        executionSteps = eventArgs2.executionSteps;

                        //execute behaviors
                        executeBehaviors(executionSteps);

                    } catch (e) {
                        //TODO : log
                        vpGrid.logException(e);
                    }
                }

            } catch (e) {
                //error in  Behaviors layer
                vpGrid.logException(e);
            }
            finally {
                return behaviors;
            }
        })();
        //end Behaviors layer

        //internal behaviors definitions
        Behaviors.securityTrimmingBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements security trimming within the vp-grid-container. Security trimming is a way to hide UI elements when user is not in a defined role.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    return;
                }

                if (isNotString(sequenceName)) {
                    vpGrid.logError({
                        message: "input sequenceName is not a string. SecurityTrimmingBehavior is aborted."
                    });
                    return;
                }

                //step 1 : find all UI elements that must be security trimmed in the sequence
                var selector = "[" + vpGrid.HtmlDataTag.securityTrimmed + "=true]";
                var jqElements = jqContainer.find(selector);
                if (jqElements.notFound()) {
                    return;
                }

                //step 2 : apply security trimming on each found elements
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    // check if security trimming must be applied on input sequenceName
                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridPreInit, "securityTrimmingBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    //step 3 : by default hide this UI element
                    jqElement.hideEx();

                    //step 4 : raise event so that listeners can decide to trim or not to trim the element
                    var eventArgs = {
                        userIsInRole: false,
                    }
                    var dataRoles = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.securityTrimmedRoles, "");
                    if (dataRoles.isNullOrEmptyOrWhitespace()) {
                        vpGrid.logError({
                            message: "data attribute : " + vpGrid.HtmlDataTag.securityTrimmedRoles + " is not defined on element : \n" + outerHtml(element)
                        });
                        dataRoles = "";
                    }

                    var roles = dataRoles.toArray(",");
                    eventArgs.roles = roles;

                    // ask listeners if user is in role
                    onSecurityTrimming(element, eventArgs);

                    //check if the userIsInRole has been setup
                    if (isNullOrUndefinedOrEmpty(eventArgs)) {
                        vpGrid.logError({
                            functionName: "securityTrimmingBehavior",
                            message: "eventArgs object corrupted by listener"
                        });
                        continue;
                    }

                    if (isNotBoolean(eventArgs.userIsInRole)) {
                        vpGrid.logError({
                            functionName: "securityTrimmingBehavior",
                            message: "eventArgs.userIsInRole boolean property corrupted by listener"
                        });
                        continue;
                    }

                    if (eventArgs.userIsInRole) {
                        // user is in role : element must be shown to user
                        jqElement.showEx();
                    }

                } // end for


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.securityTrimmingBehavior

        function onSecurityTrimming(element, eventArgs) {
            try {

                EventHandlers.raiseEvent(vpGrid.Event.onSecurityTrimming, element, eventArgs);

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        Behaviors.DisableElementBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior disables any UI element that is configured to be disabled within the vp-grid-container.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    //TODO : log info
                    return;
                }

                //step 1 : get all elements tagged with ui-disabled
                var elementsSelector = "[" + vpGrid.HtmlDataTag.UIDisabled + "=true]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoading, "DataSourceBindingsBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    applyDisabledStateOnElement(element);

                } // end for (var i = 0; i < elementsCount; i++)


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.DisableElementBehavior

        Behaviors.ActiveHeaderBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements active header within the vp-grid-container.
            /// Active header is a column header that toggles itself to a button on hoovering.
            ///</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    //TODO : log info
                    return;
                }

                //step 1 : get all elements tagged with ui-element
                var elementsSelector = "[role=" + vpGrid.HtmlRoleAttribute.ActiveHeader + "][" + vpGrid.HtmlDataTag.UIElement + "=true]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoading, "DataSourceBindingsBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    applyActiveHeaderOnElement(element);

                } // end for (var i = 0; i < elementsCount; i++)


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.ActiveHeaderBehavior

        Behaviors.DataSourceBindingsBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements data source binding within the vp-grid-container.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    //TODO : log info
                    return;
                }

                //step 1 : get all elements tagged with ui-element
                var elementsSelector = "[" + vpGrid.HtmlDataTag.UIElement + "=true]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoading, "DataSourceBindingsBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }


                    //check if ui element must bind to a data source
                    var dataSource = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DataSource, "");

                    if (dataSource.isNotNullOrEmptyOrWhitespace()) {
                        bindDataSourceOnElement(element);
                    }

                } // end for (var i = 0; i < elementsCount; i++)


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.DataSourceBindingsBehavior

        Behaviors.UIWidgetsBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements jQuery-ui widgets binding within the vp-grid-container.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    return;
                }

                //step 1 : get all elements tagged with ui-widget
                var elementsSelector = "[" + vpGrid.HtmlDataTag.UIWidget + "]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoading, "UIWidgetsBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    bindWidgetOnElement(element);
                }


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.UIWidgetsBehavior

        Behaviors.BindHandlersBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements event handlers binding within the vp-grid-container.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    //TODO : log
                    return;
                }

                //step 1 : get all elements tagged with data-vp-grid-handlers
                var elementsSelector = "[" + vpGrid.HtmlDataTag.Handlers + "]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoading, "BindHandlersBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    bindHandlersOnElement(element);
                }


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.BindHandlersBehavior

        Behaviors.FillContentPlaceholderBehavior = function (sequenceName) {
            /// <signature>
            /// <summary>This behavior implements content injection within the vp-grid-container.</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// </signature>
            try {
                var jqContainer = vpGrid.currentContainer();
                if (jqContainer.notFound()) {
                    //TODO : log
                    return;
                }

                //step 1 : get all elements tagged with data-vp-grid-ui-content-placeholder
                var elementsSelector = "[" + vpGrid.HtmlDataTag.UIContentPlaceholder + "=true]";
                var jqElements = $(elementsSelector);
                if (jqElements.notFound()) {
                    //TODO : log info
                    return;
                }

                // step 2 : take only elements that must be processed in this specific timeline sequence
                var elementsCount = jqElements.length;
                for (var i = 0; i < elementsCount; i++) {
                    var element = jqElements[i];
                    var jqElement = $(element);

                    var dataSequenceName = Behaviors.getTimelineSequenceOnElement(element, Behaviors.Sequence.GridLoaded, "FillContentPlaceholderBehavior")

                    if (dataSequenceName !== sequenceName) {
                        continue;
                    }

                    fillContentPlaceholderOnElement(element);
                }


            } catch (e) {
                vpGrid.logException(e);
            }

        } //end Behaviors.BindHandlersBehavior

        Behaviors.GridInitBehavior = function (sequenceName, timelineExecutionCallback) {
            /// <signature>
            /// <summary>This behavior implements jqGrid configuration and instanciation</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// <param name="sequenceName" type="Function">Core timeline execution engine to be called when execution of this behavior is async</param>
            /// </signature>
            try {
                JQGrid.init();

            } catch (e) {
                vpGrid.logException(e);
            }

        }

        Behaviors.GridLoadBehavior = function (sequenceName, timelineExecutionCallback) {
            /// <signature>
            /// <summary>This behavior implements jqGrid data loading</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// <param name="sequenceName" type="Function">Core timeline execution engine to be called back when execution of this behavior is async</param>
            /// </signature>
            try {
                JQGrid.loadData(timelineExecutionCallback);

            } catch (e) {
                vpGrid.logException(e);
            }

        }

        Behaviors.GridLoadedBehavior = function (sequenceName, timelineExecutionCallback) {
            /// <signature>
            /// <summary>This behavior implements jqGrid configuration after data is loaded</summary>
            /// <param name="sequenceName" type="String">Name of a sequence that is currently executed by the timeline.</param>
            /// <param name="sequenceName" type="Function">Core timeline execution engine to be called back when execution of this behavior is async</param>
            /// </signature>
            try {
                JQGrid.bindAutocompleteOnSearchInputs();

            } catch (e) {
                vpGrid.logException(e);
            }

        }

        //end internal behaviors definitions

        //private Eventhandlers
        var PrivateEventHandlers = {};
        PrivateEventHandlers.injectInternalBehaviors = function (source, eventArgs) {
            try {
                if (isNullOrUndefined(eventArgs)) {
                    //TODO : log
                    return;
                }

                var _sequenceName = eventArgs.sequenceName;
                if (isNotString(_sequenceName)) {
                    //TODO : log
                    return;
                }

                //register securityTrimming behavior at all steps
                eventArgs.behaviors.push(
                    {
                        execute: Behaviors.securityTrimmingBehavior,
                        isAsync: false
                    });

                //register UIElements behavior at all steps
                eventArgs.behaviors.push(
                        {
                            execute: Behaviors.DataSourceBindingsBehavior,
                            isAsync: false
                        });

                eventArgs.behaviors.push(
                        {
                            execute: Behaviors.ActiveHeaderBehavior,
                            isAsync: false
                        });

                //register UIWidgets behavior at all steps
                eventArgs.behaviors.push(
                        {
                            execute: Behaviors.UIWidgetsBehavior,
                            isAsync: false
                        });

                //register BindHandlers behavior at all steps
                eventArgs.behaviors.push(
                        {
                            execute: Behaviors.BindHandlersBehavior,
                            isAsync: false
                        });

                //register DisableElement behavior at all steps
                eventArgs.behaviors.push(
                        {
                            execute: Behaviors.DisableElementBehavior,
                            isAsync: false
                        });

                if (_sequenceName === Behaviors.Sequence.GridInit) {
                    eventArgs.behaviors.push(
                        {
                            execute: Behaviors.GridInitBehavior,
                            isAsync: false
                        });

                }

                if (_sequenceName === Behaviors.Sequence.GridLoading) {
                    eventArgs.behaviors.push(
                        {
                            execute: Behaviors.GridLoadBehavior,
                            isAsync: true
                        });
                }

                if (_sequenceName === Behaviors.Sequence.GridLoaded) {
                    eventArgs.behaviors.push(
                        {
                            execute: Behaviors.GridLoadedBehavior,
                            isAsync: false
                        });
                }


            } catch (e) {
                vpGrid.logException(e);
            }
        }

        function registerPrivateHandlers() {
            try {

                vpGrid.registerEventHandler(PrivateEvent.onBehaviorsRegistering, PrivateEventHandlers.injectInternalBehaviors)

            } catch (e) {
                vpGrid.logException(e);
            }

        }

        function onModalDialogClosed(element, eventArgs) {
            try {
                if (isNullOrUndefinedOrEmpty(element)) {
                    vpGrid.logError({
                        functionName: "onModalDialogClosed",
                        message: "input element is null or empty or undefined",
                        info: "listeners to this event are not called"
                    });
                    return;
                }

                var jqElement = $(element);
                if (jqElement.notFound()) {
                    vpGrid.logError({
                        functionName: "onModalDialogClosed",
                        message: "Input element not found \n: " + outerHtml(element)
                    });
                    return;
                }

                if (isNullOrUndefined(eventArgs)) {
                    eventArgs = {};
                }

                var dialogId = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DialogId,"");
                if (dialogId.isNullOrEmptyOrWhitespace()) {
                    vpGrid.logError({
                        functionName: "onModalDialogClosed",
                        message: "data attribute : " + vpGrid.HtmlDataTag.DialogId + " is not defined on element \n: " + outerHtml(element)
                    });
                }

                eventArgs.dialogId = dialogId;

                EventHandlers.raiseEvent(vpGrid.Event.onModalDialogClosed, element, eventArgs);

            } catch (e) {
                vpGrid.logException(e);
            }
        }


        // end vpGrid Events

        // jqGrid layer
        var JQGrid = (function (vpGridOptions) {
            var jqGrid = {};
            try {
                jqGrid.options = {
                    datatype: "local",
                    //height: 275,    // 12 rows
                    height: 390,    // 17 rows
                    //height: 181,    // 8 rows
                    //width: 500,
                    autowidth: true,
                    shrinkToFit: true,
                    ignoreCase: true,
                    altRows: false,
                    loadonce: true, //all data is loaded locally
                    rowNum: 10,
                    caption: '',
                    multiselect: true,
                    multiboxonly: true,
                    hidegrid: false,
                    cellEdit: true,
                    cellsubmit: 'clientArray',
                    gridview: true,
                    viewrecords: false,
                    toolbar: [true, "bottom"], // bottom toolbar is used as a status bar only
                    // add-on for vpGrid
                    rowHeight: 23,  // one row in jqGrid is 23 pixels height
                    minColWidth : 60
                }

                jqGrid.Id = "vpGridExcel";

                // total number of items loaded in the grid from remote server
                // this filed is updated by jqGrid.loadDataFromWebResponse function
                jqGrid.totalItemCount = 0;

                // total number of items seen in the grid by filtering through the search filter toolbar
                // this field is update by jqGrid.rows function
                jqGrid.filteredItemCount = 0;

                jqGrid.hasFrozenColumns = false;

                jqGrid.currentRowId = null;

                jqGrid.options.beforeEditCell = function (rowid, cellname, value, iRow, iCol) {
                    try {

                        jqGrid.currentRowId = rowid;

                        var colModels = jqGrid.currentColModels();
                        if (isNullOrUndefinedOrEmpty(colModels)) {
                            vpGrid.logError({
                                message: "ColModels not found : beforeEditCell is stopped. rowid='" + rowid + "'" + "; cellName='" + cellname + "'"
                            });
                            return;
                        }

                        var colModel = colModels[iCol];

                        var editType = colModel.edittype;
                        if (isNullOrUndefinedOrEmpty(editType)) {
                            return;
                        }

                        var data = jqGrid.rowData(rowid);

                        var eventArgs = {
                            cellName: cellname,
                            cellValueBeforeEditing: value,
                            rowData: data
                        };

                        if (editType === "select") {
                            //raise event to enable listeners to inspect and/or to inject values for the select tag
                            onSelectCellRendering(colModel, eventArgs);
                            return;
                        }

                        //TODO : Process other input types

                    } catch (e) {
                        vpGrid.logException(e);
                    }

                }

                jqGrid.options.afterEditCell = function (rowid, cellname, value, iRow, iCol) {
                    try {
                        jqGrid.currentRowId = rowid;

                        // find the cell
                        var jqGridHost = jqGrid.host();
                        // first step : find the row where the cell is in edit mode
                        var rowSelector = "tr[role=row][id=" + rowid + "]";
                        var jqRow = jqGridHost.find(rowSelector);
                        // second step : find the cell in edit mode
                        var cellSelector = "td[role=gridcell][aria-describedby=" + jqGrid.Id + "_" + cellname + "]";
                        var jqCell = jqRow.find(cellSelector);
                        if (jqCell.notFound()) {
                            vpGrid.logError({
                                message: "Cell not found in DOM. rowid='" + rowid + "'" + "; cellName='" + cellname + "'"
                            });
                        }

                        //third step : find the input element (either input or select or textarea)
                        var jqInput = jqCell.find("input");
                        var jqSelect = jqCell.find("select");
                        var jqTextArea = jqCell.find("textarea");

                        if (jqInput.notFound() && jqSelect.notFound() && jqTextArea.notFound()) {
                            vpGrid.logError({
                                message: "Input element or Select or Textarea not found in DOM. rowid='" + rowid + "'" + "; cellName='" + cellname + "'"
                            });
                            return;
                        }

                        //adjust the width of the input field
                        jqSelect.width(jqSelect.parent().width());

                        var colModels = jqGrid.currentColModels();
                        if (isNullOrUndefinedOrEmpty(colModels)) {
                            vpGrid.logError({
                                message: "ColModels not found : rowid='" + rowid + "'" + "; cellName='" + cellname + "'"
                            });
                            return;
                        }

                        var colModel = colModels[iCol];
                        if (isNullOrUndefinedOrEmpty(colModel)) {
                            vpGrid.logError({
                                message: "ColModel not found :  rowid='" + rowid + "'" + "; cellName='" + cellname + "'" + "; iCol='" + iCol + "'"
                            });
                            return;
                        }


                        if (colModel.isAutocomplete) {
                            // input is an autocomplete field
                            //bindEventsOnInputDateEditing(jqInput, iRow, iCol);
                            bindEventsOnInputEditing(jqInput, iRow, iCol);
                            // bind autocomplete widget
                            jqGrid.options.bindAutocompleteCell(jqInput, value, cellname,iRow, iCol);
                            // do a click to show the full list of autocomplete values
                            executeAsync(function () {
                                try {
                                    jqInput.click();
                                } catch (e) {
                                    vpGrid.logException(e);
                                }
                                
                                }, 200);

                            
                            return;
                        }


                        if (colModel.isDate) {
                            // input is a date field
                            bindEventsOnInputDateEditing(jqInput, iRow, iCol);
                            // bind datepicker widget
                            var datepickerOptions = vpGridOptions.datepickerOptions;
                            if (isNullOrUndefined(datepickerOptions)) {
                                datepickerOptions = {};
                            }
                            //TODO : raise event so that listeners can inspect and/or modifiy datepicker options for this cell

                            // Autosave cell on datepicker closed
                            datepickerOptions.onClose = function (selectedDate) {
                                try {
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);
                                } catch (e) {
                                    vpGrid.logException(e);
                                }
                            };

                            jqInput.datepicker(datepickerOptions);
                            return;
                        }



                        if (colModel.isSelect) {
                            bindEventsOnSelectEditing(jqSelect, iRow, iCol);
                            return;
                        }

                        if (colModel.isNote) {
                            bindEventsOnTextAreaEditing(jqTextArea, iRow, iCol);
                            return;
                        }

                        if (colModel.isInteger) {
                            bindEventsOnIntegerEditing(jqInput, iRow, iCol);
                            return;
                        }

                        if (colModel.isCurrency) {

                            //TODO : extract following lines into a separate method
                            var customFormatterOptions = {};
                            customFormatterOptions.currency = {};
                            var customCurrencyFormatterOptions = customFormatterOptions.currency;
                            customCurrencyFormatterOptions.decimalSeparator = colModel.formatoptions.decimalSeparator;
                            customCurrencyFormatterOptions.thousandsSeparator = colModel.formatoptions.thousandsSeparator;
                            customCurrencyFormatterOptions.decimalPlaces = colModel.formatoptions.decimalPlaces;

                            var localeValue = $.fn.fmatter("currency", value, customFormatterOptions);
                            //TODO : end extract

                            jqInput.val(localeValue);
                            bindEventsOnCurrencyEditing(jqInput, iRow, iCol);
                            return;
                        }

                        // bind events on other type of inputs (checkbox/text ...)
                        bindEventsOnInputEditing(jqInput, iRow, iCol);

                    } catch (e) {
                        vpGrid.logException(e);
                    }

                }

                jqGrid.options.beforeSaveCell= function (rowid, cellname, value, iRow, iCol) {
                    try {
                        var colModels = jqGrid.currentColModels();
                        if (isNullOrUndefinedOrEmpty(colModels)) {
                            vpGrid.logError({
                                message: "ColModels not found : rowid='" + rowid + "'" + "; cellName='" + cellname + "'"
                            });
                            return;
                        }

                        var colModel = colModels[iCol];
                        if (isNullOrUndefinedOrEmpty(colModel)) {
                            vpGrid.logError({
                                message: "ColModel not found :  rowid='" + rowid + "'" + "; cellName='" + cellname + "'" + "; iCol='" + iCol + "'"
                            });
                            return;
                        }
                        var sanitizedValue = value;

                        if (colModel.isCurrency) {
                            //unformat the input value
                            var thousandsSeparator = colModel.formatoptions.thousandsSeparator;
                            var decimalSeparator = colModel.formatoptions.decimalSeparator;
                            var sanitizedValue = value.replaceAll(thousandsSeparator, "");
                            sanitizedValue = sanitizedValue.replaceAll(decimalSeparator, '.');
                        }

                        //TODO :  raise event
                        var data = jqGrid.rowData(rowid);
                        var eventArgs = {
                            cellName: cellname,
                            cellValue: sanitizedValue,
                            rowData: data,
                            
                        };
                        //raise event to enable listeners to inspect and/or to inject values for the attributes of image tag
                        onCellUpdating(eventArgs);

                        //check if the eventArgs object has been changed by listeners
                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "jqGrid.options.beforeSaveCell",
                                message: "eventArgs object corrupted by listener onCellUpdating"
                            });
                            return sanitizedValue;
                        }

                        if (isNotString(eventArgs.cellValue)) {
                            vpGrid.logError({
                                functionName: "jqGrid.options.beforeSaveCell",
                                message: "eventArgs.cellValue object corrupted by listener",
                                info: "listeners on onCellUpdating should return a String type for the eventArgs.cellValue property"
                            });
                            return sanitizedValue;
                        }

                        // take value given by listeners
                        sanitizedValue = eventArgs.cellValue;
                        return sanitizedValue;

                    } catch (e) {
                        vpGrid.logException(e);
                        return value;
                    }
                }

                jqGrid.options.gridComplete = function () {
                    try {
                        jqGrid.filteredItemCount = -1;

                        jqGrid.isReady = true;
                        ensureOnlyOneRowCanBeChecked();

                        if (jqGrid.filteredItemCount === -1) {
                            updateFilteredItemCount();
                        }
                        executeAsync(updateContentPlaceholders, 100);

                        if (jqGrid.isEmpty()) {
                            showEmptyMessageOnGridComplete()
                            return;
                        }

                        hideEmptyMessageOnGridComplete();


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.options.afterValidateCell = function (value, colTitle, iCol) {
                    /// <signature>
                    /// <summary>jqGrid extension (from vpGrid) to let listeners do custom validation. This method is called when jqGrid has done its own cell validation</summary>
                    /// <param name="value" type="String">Value of the cell being edited by the user</param>
                    /// <param name="colTitle" type="String">Title of the column's cell</param>
                    /// <param name="iCol" type="Number">Index of the column in the colmodels</param>
                    /// <returns type="Array">Returns an array of values :first value - true or false. The value of true mean that the checking is successful false otherwise; the second value have sense only, if the first value is false, represents the error message which will be displayed to the user. Typically this can look like this [false,”Please enter valid value”]</returns>
                    /// </signature>
                    try {
                        var colModels = jqGrid.currentColModels();
                        if (isNullOrUndefinedOrEmpty(colModels)) {
                            vpGrid.logError({
                                message: "ColModels not found : afterValidateCell is stopped. colTitle='" + colTitle + "'" 
                            });
                            return;
                        }

                        var colModel = colModels[iCol];
                        var cellName = colModel.name;
                        var rowid = jqGrid.currentRowId;
                        var data = jqGrid.rowData(rowid);

                        var eventArgs = {
                            cellName: cellName,
                            cellValue: value,
                            rowData: data,
                            isValid: true,
                            errorMessage : ""
                        };

                        //raise event
                        onCellValidating(eventArgs);

                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "jqGrid.options.afterValidateCell",
                                message: "eventArgs object corrupted by listener"
                            });
                            return [true, "", ""];
                        }

                        if (isNotBoolean(eventArgs.isValid)) {
                            vpGrid.logError({
                                functionName: "jqGrid.options.afterValidateCell",
                                message: "eventArgs.isValid property corrupted by listener"
                            });
                            return [true, "", ""];;
                        }

                        if (eventArgs.isValid) {
                            return [true, "", ""];
                        }

                        return [false, eventArgs.errorMessage, ""];

                    } catch (e) {
                        vpGrid.logException(e);
                        //fallback to a successfull validation to prevent user being locked inside a cell
                        return [true, "", ""];
                    }
                }

                jqGrid.options.bindAutocompleteCell = function (input, value, cellName, iRow, iCol) {
                    /// <signature>
                    /// <summary>jqGrid extension (from vpGrid) to let listeners configure autocomplete. This method is called when jqGrid is in cell editing mode</summary>
                    /// <param name="input" type="Object">The input DOM element</param>
                    /// <param name="value" type="String">Value of the cell being edited by the user</param>
                    /// <param name="cellname" type="String">Cell name</param>
                    /// <param name="iCol" type="Number">Index of the column in the colmodels</param>
                    /// <returns type="Array">Returns an array of values :first value - true or false. The value of true mean that the checking is successful false otherwise; the second value have sense only, if the first value is false, represents the error message which will be displayed to the user. Typically this can look like this [false,”Please enter valid value”]</returns>
                    /// </signature>
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);


                        var eventArgs = {
                            cellName: cellName,
                            cellValue: value,
                            options : {},
                            data : []
                        };

                        //raise event
                        onAutocompleteCellRendering(eventArgs);

                        if (isNullOrUndefinedOrEmpty(eventArgs.data)) {
                            eventArgs.data = jqGrid.colValues(cellName);
                        }

                        //TODO : serialize the options object to a data attribute on input element
                        bindAutoCompleteWidgetWithDataOnElement(input, eventArgs.data);

                        
                        // bind to select event to automatically save the cell when an item is selected in the autocomplete list
                        jqInput.on("autocompleteselect", function (event, ui) {
                            try {
                                executeAsync(function () {
                                    try {
                                        jqGrid.saveCell(iRow, iCol);
                                    } catch (e) {
                                        vpGrid.logException(e);
                                    }
                                }, 50);
                                

                            } catch (e) {
                                vpGrid.logException(e);
                            }

                        });

                    } catch (e) {
                        vpGrid.logException(e);
                       
                    }
                }


                jqGrid.saveCell = function (iRow, iCol) {
                    try {
                        var jqGridHost = jqGrid.host();
                        jqGridHost.jqGrid("saveCell", iRow, iCol);
                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }


                function onCellValidating(eventArgs) {
                    try {

                        EventHandlers.raiseEvent(vpGrid.Event.onCellValidating, vpGrid, eventArgs);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function updateContentPlaceholders() {
                    try {
                        var jqGridContainer = jqGrid.container();
                        var elementsSelector = "[" + vpGrid.HtmlDataTag.UIContentPlaceholder + "=true]";
                        var jqElements = jqGridContainer.find(elementsSelector);
                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        // step 2 : take only elements that must be processed in this specific timeline sequence
                        var elementsCount = jqElements.length;
                        for (var i = 0; i < elementsCount; i++) {
                            var element = jqElements[i];

                            fillContentPlaceholderOnElement(element);
                        }
                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function updateFilteredItemCount() {
                    try {
                        var itemCount = jqGrid.rows.length - 1;
                        jqGrid.filteredItemCount = itemCount;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.imageFormatter = function (cellValue, options, rowObject) {
                    try {
                        if (isNullOrUndefinedOrEmpty(cellValue)) {
                            cellValue = "";
                        }

                        var colModel = options.colModel;
                        var cellName = colModel.name;
                        var data = rowObject;

                        var eventArgs = {
                            cellName: cellName,
                            cellValue: cellValue,
                            rowData: data,
                            img: {
                                alt: cellValue,
                                title: cellValue,
                                src: ""
                            }
                        };

                        //raise event to enable listeners to inspect and/or to inject values for the attributes of image tag
                        onImageCellRendering(eventArgs);

                        //check if the src attribute has been setup
                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "jqGrid.imageFormatter",
                                message: "eventArgs object corrupted by listener"
                            });
                            return "";
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.img)) {
                            vpGrid.logError({
                                functionName: "jqGrid.imageFormatter",
                                message: "eventArgs.img object corrupted by listener"
                            });
                            return "";
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.img.src)) {
                            return "";
                        }

                        //TODO : log if img.alt attribute has been changed

                        //render the img tag
                        var image = $("<img>", eventArgs.img);
                        var span = $('<span>');
                        var wrappedImage = image.appendTo(span);
                        return wrappedImage.parent().html();


                    } catch (e) {
                        vpGrid.logException(e);
                        return "";
                    }
                }

                jqGrid.imageUnformatter = function (cellValue, options, cell) {
                    try {
                        var jqCell = $(cell);
                        var jqImage = jqCell.find('img');
                        var altvalue = jqImage.toStringOrDefaultFromAttribute("alt", "");
                        return altvalue;

                    } catch (e) {
                        vpGrid.logException(e);
                        return cellValue;
                    }
                }

                jqGrid.linkFormatter = function (cellValue, options, rowObject) {
                    try {
                        if (isNullOrUndefinedOrEmpty(cellValue)) {
                            cellValue = "";
                        }

                        var colModel = options.colModel;
                        var cellName = colModel.name;
                        var data = rowObject;

                        var target = "_vpTab";
                        var formatOptions = colModel.formatOptions || {};
                        if (isNotNullOrUndefinedOrEmpty(formatOptions.target)) {
                            target = formatOptions.target;
                        }

                        //extract link content
                        var linkContent = cellValue.leafName();

                        var eventArgs = {
                            cellName: cellName,
                            cellValue: cellValue,
                            rowData: data,
                            a: {
                                href: cellValue,
                                title: cellValue,
                                target: target
                            },
                            html: linkContent
                        };

                        //raise event to enable listeners to inspect and/or to inject values for the attributes of <a> tag
                        onLinkCellRendering(eventArgs);

                        //check if the src attribute has been setup
                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "jqGrid.linkFormatter",
                                message: "eventArgs object corrupted by listener"
                            });
                            return "";
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.a)) {
                            vpGrid.logError({
                                functionName: "jqGrid.linkFormatter",
                                message: "eventArgs.a object corrupted by listener"
                            });
                            return "";
                        }

                        //TODO : log if a.href attribute has been changed

                        if (isNullOrUndefinedOrEmpty(eventArgs.a.href)) {
                            return "";
                        }

                        //TODO : log if eventArgs.html attribute has been changed
                        if (isNullOrUndefinedOrEmpty(eventArgs.html)) {
                            return "";
                        }

                        //render the a tag
                        var link = $("<a>", eventArgs.a);
                        link.html(eventArgs.html)
                        var span = $('<span>');
                        var wrappedLink = link.appendTo(span);
                        return wrappedLink.parent().html();

                    } catch (e) {
                        vpGrid.logException(e);
                        return "";
                    }
                }

                jqGrid.linkUnformatter = function (cellValue, options, cell) {
                    try {
                        var jqCell = $(cell);
                        var jqLink = jqCell.find('a');
                        var hrefValue = jqLink.toStringOrDefaultFromAttribute("href", "");
                        return hrefValue;

                    } catch (e) {
                        vpGrid.logException(e);
                        return cellValue;
                    }
                }

                function onImageCellRendering(eventArgs) {
                    try {

                        EventHandlers.raiseEvent(vpGrid.Event.onImageCellRendering, vpGrid, eventArgs);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function onLinkCellRendering(eventArgs) {
                    try {

                        EventHandlers.raiseEvent(vpGrid.Event.onLinkCellRendering, vpGrid, eventArgs);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function onAutocompleteCellRendering(eventArgs) {
                    try {
                        // make a backup copy of eventArgs
                        var eventArgsCopy = $.extend({}, eventArgs);

                        EventHandlers.raiseEvent(vpGrid.Event.onAutocompleteCellRendering, vpGrid, eventArgs);

                        // check possible misbehavior from listeners
                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            vpGrid.logError({
                                functionName: "onAutocompleteCellRendering",
                                message: "eventArgs object corrupted by listener",
                                info : "eventArgs object is reverted to its original state"
                            });
                            $.extend(eventArgs, eventArgsCopy);
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.options)) {
                            eventArgs.options = eventArgsCopy.options;
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs.data)) {
                            eventArgs.data = eventArgsCopy.data;
                        }

                        if (isNotArray(eventArgs.data)) {
                            vpGrid.logError({
                                functionName: "onAutocompleteCellRendering",
                                message: "eventArgs.data array corrupted by listener",
                                info: "eventArgs.data is reverted to its original state"
                            });
                            eventArgs.data = eventArgsCopy.data;
                        }


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }






                function onCellUpdating(eventArgs) {
                    try {

                        EventHandlers.raiseEvent(vpGrid.Event.onCellUpdating, vpGrid, eventArgs);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.defaultCurrencyFormatOptions = function(){
                    try {
                        
                        var formatOptions = $.jgrid.formatter.currency;
                        return formatOptions;

                    } catch (e) {
                        vpGrid.logException(e);
                        return null;
                    }
                }

                function bindEventsOnInputDateEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        //unbind events bound by jqGrid
                        jqInput.unbind("keydown");

                        // re-bind the keydown event with our implementation
                        //backup original value
                        var originalValue = jqInput.val();

                        jqInput.bind("keydown", function (e) {
                            try {
                                // case Escape
                                if (e.keyCode === 27) {
                                    //hide the datepicker
                                    hideWidgetOnElement(this);
                                    //jqGridHost.jqGrid("restoreCell", iRow, iCol);
                                    jqGridHost.jqGrid("setCell", iRow, iCol, originalValue, false, false, true);

                                    return false;
                                }

                                // case ENTER
                                if (e.keyCode === 13) {
                                    hideWidgetOnElement(this);
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);
                                    return false;
                                }

                                // case TAB
                                if (e.keyCode === 9) {
                                    //TODO : check if cell has been modified
                                    hideWidgetOnElement(this);
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);

                                    //case Shift TAB
                                    if (e.shiftKey) {
                                        jqGridHost.jqGrid("prevCell", iRow, iCol);
                                        return false;
                                    }

                                    jqGridHost.jqGrid("nextCell", iRow, iCol);
                                    return false;
                                }


                            } catch (ex) {
                                vpGrid.logException(ex);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function bindEventsOnSelectEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        //unbind events bound by jqGrid
                        jqInput.unbind("keydown");

                        // re-bind the keydown event with our implementation
                        jqInput.bind("keydown", function (e) {
                            try {
                                // case Escape
                                if (e.keyCode === 27) {
                                    jqGridHost.jqGrid("restoreCell", iRow, iCol);
                                    return false;
                                }

                                // case ENTER
                                if (e.keyCode === 13) {
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);
                                    return false;
                                }

                                // case TAB
                                if (e.keyCode === 9) {
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);
                                    //case Shift TAB
                                    if (e.shiftKey) {
                                        jqGridHost.jqGrid("prevCell", iRow, iCol);
                                        return false;
                                    }

                                    jqGridHost.jqGrid("nextCell", iRow, iCol);
                                    return false;
                                }

                                // case KeyUP
                                if (e.keyCode === 38) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    // we must prevent the onchange event in this case
                                    $(this).unbind('change');
                                    return true;
                                }

                                // case Key Down
                                if (e.keyCode === 40) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    // we must prevent the onchange event in this case
                                    $(this).unbind('change');
                                    return true;
                                }

                                // case left arrow
                                if (e.keyCode === 37) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    return false;
                                }

                                // case right arrow
                                if (e.keyCode === 39) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    return false;
                                }

                            } catch (ex) {
                                vpGrid.logException(ex);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });

                        jqInput.bind('blur', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);
                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });

                        //automaticall save the cell when an option has been selected in the Select element
                        jqInput.bind('change', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);

                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function bindEventsOnTextAreaEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        //unbind events bound by jqGrid
                        jqInput.unbind("keydown");

                        // re-bind the keydown event with our implementation
                        jqInput.bind("keydown", function (e) {
                            try {
                                // case Escape
                                if (e.keyCode === 27) {
                                    jqGridHost.jqGrid("restoreCell", iRow, iCol);
                                    return false;
                                }

                                // case ENTER
                                if (e.keyCode === 13) {
                                    //do nothing
                                    //jqGridHost.jqGrid("saveCell", iRow, iCol);
                                    return true;
                                }

                                // case TAB
                                if (e.keyCode === 9) {
                                    jqGridHost.jqGrid("saveCell", iRow, iCol);
                                    //case Shift TAB
                                    if (e.shiftKey) {
                                        jqGridHost.jqGrid("prevCell", iRow, iCol);
                                        return false;
                                    }

                                    jqGridHost.jqGrid("nextCell", iRow, iCol);
                                    return false;
                                }


                                // case KeyUP
                                if (e.keyCode === 38) {
                                    // do nothing (user wants to navigate in the text with the up/dow key
                                    return true;
                                }

                                // case Key Down
                                if (e.keyCode === 40) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    return true;
                                }

                                // case left arrow
                                if (e.keyCode === 37) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    return true;
                                }

                                // case right arrow
                                if (e.keyCode === 39) {
                                    // do nothing (user wants to select an option with the up/dow key
                                    return true;
                                }

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });

                        jqInput.bind('blur', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);
                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });



                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function bindEventsOnCurrencyEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        jqInput.bind('blur', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);
                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });



                        //unbind events bound by jqGrid
                        jqInput.unbind("keypress");

                        // re-bind the keydown event with our implementation
                        jqInput.bind("keypress", function (e) {
                            try {
                                
                                    var keyCode = e.which;
		                            if (keyCode === 48 ) { //0
			                            return true;
		                            }
		                            if (keyCode === 49 ) { //1
			                            return true;
		                            }
		                            if (keyCode === 50 ) { //2
			                            return true;
		                            }
		                            if (keyCode === 51 ) { //3
			                            return true;
		                            }
		                            if (keyCode === 52 ) { //4
			                            return true;
		                            }
		                            if (keyCode === 53 ) { //5
			                            return true;
		                            }
		                            if (keyCode === 54 ) { //6
			                            return true;
		                            }
		                            if (keyCode === 55 ) { //7
			                            return true;
		                            }
		                            if (keyCode === 56 ) { //8
			                            return true;
		                            }
		                            if (keyCode === 57 ) { //9
			                            return true;
		                            }
		                            if (keyCode === 8 ) { //backspace
			                            return true;
		                            }

		                            if (keyCode === 32) { //space
		                                return true;
		                            }

		                            if (keyCode === 44) { //comma
		                                return true;
		                            }

		                            if (keyCode === 46) { //point
		                                return true;
		                            }

                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function bindEventsOnIntegerEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        jqInput.bind('blur', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);
                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });

                        //unbind events bound by jqGrid
                        jqInput.unbind("keypress");

                        // re-bind the keydown event with our implementation
                        jqInput.bind("keypress", function (e) {
                            try {

                                var keyCode = e.which;
                                if (keyCode === 48) { //0
                                    return true;
                                }
                                if (keyCode === 49) { //1
                                    return true;
                                }
                                if (keyCode === 50) { //2
                                    return true;
                                }
                                if (keyCode === 51) { //3
                                    return true;
                                }
                                if (keyCode === 52) { //4
                                    return true;
                                }
                                if (keyCode === 53) { //5
                                    return true;
                                }
                                if (keyCode === 54) { //6
                                    return true;
                                }
                                if (keyCode === 55) { //7
                                    return true;
                                }
                                if (keyCode === 56) { //8
                                    return true;
                                }
                                if (keyCode === 57) { //9
                                    return true;
                                }
                                if (keyCode === 8) { //backspace
                                    return true;
                                }

                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function bindEventsOnInputEditing(input, iRow, iCol) {
                    try {
                        if (isNullOrUndefinedOrEmpty(input)) {
                            //TODO : log
                            return;
                        }

                        var jqInput = $(input);
                        if (jqInput.notFound()) {
                            //TODO : log
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        jqInput.bind('blur', function () {
                            try {
                                jqGridHost.jqGrid("saveCell", iRow, iCol);
                                // Prevent default action
                                return false;

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                        });

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function onSelectCellRendering(colModel, eventArgs) {
                    try {

                        if (isNullOrUndefined(colModel)) {
                            //TODO : log
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(eventArgs)) {
                            //TODO : log
                            return;
                        }

                        eventArgs.selectOptions = [];

                        EventHandlers.raiseEvent(vpGrid.Event.onSelectCellRendering, vpGrid, eventArgs);

                        //check if new select options have been passed by the listener(s)
                        if (isNullOrUndefinedOrEmpty(eventArgs.selectOptions)) {
                            // get options value from all values in the current column
                            var colValues = jqGrid.colValues(colModel.name);
                            var optionsCount = colValues.length;
                            eventArgs.selectOptions = eventArgs.selectOptions || {};
                            for (var i = 0; i < optionsCount; i++) {
                                eventArgs.selectOptions.push({
                                    key: colValues[i],
                                    value: colValues[i]
                                });
                            }
                        }

                        var selectOptions = eventArgs.selectOptions;
                        var optionsCount = selectOptions.length;
                        var optionsValue = "";
                        for (var i = 0; i < optionsCount; i++) {
                            var selectOption = selectOptions[i];
                            optionsValue += selectOption.key + ':' + selectOption.value;
                            if (i < optionsCount - 1) {
                                optionsValue += ';';
                            }
                        }

                        //update  the colModel
                        colModel.editoptions = colModel.editoptions || {};
                        colModel.editoptions.value = optionsValue;


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function tryFindCurrentGridSchema() {
                    try {
                        var selector = "table[role=" + vpGrid.HtmlRoleAttribute.vpGridSchema + "]";
                        var jqElement = $(selector);

                        if (jqElement.found()) {
                            return jqElement;
                        }

                        vpGrid.logError({
                            message: "DOM element : <table role='" + vpGrid.HtmlRoleAttribute.vpGridSchema + "'> is missing. Please insert it in the Web page to activate the Excel grid."
                        });

                        return $(null);

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.initLocaleOptions = function () {
                    try {
                        if (isNullOrUndefinedOrEmpty(vpGridOptions)) {
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(vpGridOptions.jqGridOptions)) {
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(vpGridOptions.jqGridOptions.jgrid)) {
                            return;
                        }
                        var customOptions = vpGridOptions.jqGridOptions.jgrid;
                        if (isNullOrUndefinedOrEmpty(customOptions)) {
                            return;
                        }

                        try {
                            $.extend($.jgrid.formatter.date, customOptions.formatter.date);
                        } catch (e) {
                            vpGrid.logException(e);
                        }

                        try {
                            $.extend($.jgrid.formatter.currency, customOptions.formatter.currency);
                        } catch (e) {
                            vpGrid.logException(e);
                        }

                        try {
                            $.extend($.jgrid.formatter.integer, customOptions.formatter.integer);
                        } catch (e) {
                            vpGrid.logException(e);
                        }

                        try {
                            $.extend($.jgrid.formatter.number, customOptions.formatter.number);
                        } catch (e) {
                            vpGrid.logException(e);
                        }

                        try {
                            $.extend($.jgrid.formatter.checkbox, customOptions.formatter.checkbox);
                        } catch (e) {
                            vpGrid.logException(e);
                        }

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initMinColdWidthOption = function()
                {
                    try {
                        if (isNullOrUndefinedOrEmpty(jqGrid.options.minColWidth)) {
                            vpGrid.logError({
                                functionName: "jqGrid.initMinColdWithOption",
                                message: "property minColWidth is not defined in jqGrid.options",
                                info: "minColdWitdth is assigned a default value of 20 pixels"
                            })
                            jqGrid.options.minColWidth = 20;
                        }

                        if (isNullOrUndefinedOrEmpty(vpGridOptions)) {
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(vpGridOptions.minColWidth)) {
                            return;
                        }
                        
                        var widthValue = vpGridOptions.minColWidth;
                        if (isNotInt(widthValue)) {
                            vpGrid.logError({
                                functionName: "jqGrid.initMinColdWithOption",
                                message: "property minColWidth is not defined or has not the correct format in vpGrid Options",
                                info: "minColdWitdth is assigned a default value of " + jqGrid.options.minColWidth + " pixels"
                            })
                            return;
                        }

                        jqGrid.options.minColWidth = vpGridOptions.minColWidth;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initWidthOption = function () {
                    try {
                        
                        if (isNullOrUndefinedOrEmpty(vpGridOptions)) {
                            return;
                        }

                        if (isNotBoolean(vpGridOptions.fitToBrowserWidth)) {
                            return;
                        }

                        if (vpGridOptions.fitToBrowserWidth) {
                            jqGrid.options.autowidth = true;
                            return;
                        }

                        jqGrid.options.autowidth = false;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initShrinkToFitOption = function () {
                    try {

                        if (isNullOrUndefinedOrEmpty(vpGridOptions)) {
                            return;
                        }

                        if (isNotBoolean(vpGridOptions.fixedColWidth)) {
                            return;
                        }

                        if (vpGridOptions.fixedColWidth) {
                            jqGrid.options.shrinkToFit = false;
                            return;
                        }

                        jqGrid.options.shrinkToFit = true;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initOptions = function () {
                    try {
                        jqGrid.initLocaleOptions();
                        jqGrid.initMinColdWidthOption();
                        jqGrid.initWidthOption();
                        jqGrid.initShrinkToFitOption();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.host = function () {
                    try {
                        var jqGridHostSelector = "table[id=" + jqGrid.Id + "]";
                        var jqGridHost = $(jqGridHostSelector);

                        if (jqGridHost.notFound()) {
                            vpGrid.logError({
                                message: "DOM element : <table id='" + jqGrid.Id + "'> is missing. jqGrid cannot be managed by vpGrid."
                            });
                        }

                        return jqGridHost;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.container = function () {
                    try {
                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridBody + "]";
                        var jqGridContainer = $(selector);

                        if (jqGridContainer.notFound()) {
                            vpGrid.logError({
                                message: "Selector not found : '" + jqGridContainer.selector + "'",
                                functionName: "jqGrid.container",
                                info: "Dom element : <div role='" + vpGrid.HtmlRoleAttribute.vpGridBody + "'> is missing. Please insert it in the Web page to activate the Excel grid."

                            });
                        }

                        return jqGridContainer;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.contentArea = function () {
                    try {
                        var jqGridHost = jqGrid.host();

                        if (jqGridHost.notFound()) {
                            vpGrid.logError({
                                message: "Cannot get jqGrid contentArea because jqGrid host is not found."
                            });
                            return $(null);
                        }

                        var jqElement = jqGridHost.closest("div");

                        return jqElement;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.init = function () {
                    try {
                        jqGrid.isReady = false;

                        jqGrid.initData();

                        //find DOM element where to plug the jqGrid component
                        var jqGridSchema = tryFindCurrentGridSchema();
                        var jqGridRootNode = jqGridSchema.parent();

                        if (jqGridSchema.notFound()) {
                            //TODO : log
                            return;
                        }

                        //load jqGrid options
                        jqGrid.initOptions();

                        //Build the column models
                        buildColModels();

                        //Init the width of the grid (can be done only after buildColModels call
                        jqGrid.initWidth();

                        //Setup default Caption
                        if (vpGridOptions.defaultCaption) {
                            jqGrid.options.caption = vpGridOptions.defaultCaption;
                        }

                        //setup the height of visible rows
                        if (vpGridOptions.numberOfVisibleRows) {
                            jqGrid.options.height = jqGrid.options.rowHeight * vpGridOptions.numberOfVisibleRows - 3;
                        }

                        //extract the custom grid caption schema
                        var customCaptionSelector = "[role=" + vpGrid.HtmlRoleAttribute.vpGridSchemaCaption + "]";
                        var customCaptionGrid = $(customCaptionSelector).detach();

                        //extract the custom grid status bar
                        var customStatusBarSelector = "[role=" + vpGrid.HtmlRoleAttribute.vpGridSchemaStatusBar + "]";
                        var customStatusBar = $(customStatusBarSelector).detach();

                        //extract the group headers description -> because they must be injected after calling jqGrid constructor
                        var groupHeadersSelector = "tr[role=" + vpGrid.HtmlRoleAttribute.vpGridSchemaColumnsGroup + "] ";
                        var customGroupHeaders = $(groupHeadersSelector).detach();

                        //detach the schema table from DOM
                        jqGridSchema.detach();

                        //dynamically add an empty table to host jqGrid in the DOM
                        var table = '<table id="' + jqGrid.Id + '"></table>';
                        var jqGridHost = $(table);
                        jqGridRootNode.append(jqGridHost);

                        //Create jqGrid
                        //TODO : raise event to enable listeners inspect and/or modify the jqGrid options
                        jqGridHost.jqGrid(jqGrid.options);

                        //inject custom Caption
                        if (customCaptionGrid.found() && vpGridOptions.enableCustomCaption) {
                            var jqGridCaptionSelector = "span.ui-jqgrid-title";
                            var jqGridCaption = $(jqGridCaptionSelector).first();
                            jqGridCaption.html("");
                            customCaptionGrid.appendTo(jqGridCaption);
                        }

                        //inject custom group headers
                        var groupHeadersOptions = buildGroupHeadersOptions(customGroupHeaders);
                        if (isNotNullOrUndefinedOrEmpty(groupHeadersOptions)) {
                            jqGridHost.jqGrid('setGroupHeaders', groupHeadersOptions);
                            // inject special css tag to be able to customize the rendering of the group headers
                            jqGridRootNode.find("tr[role=rowheader]").first().find("th[role=columnheader]").addClass('vp-grid-group-header');

                        }

                        //inject custom Status Bar
                        if (customStatusBar.found() && vpGridOptions.enableCustomStatusBar) {
                            //inject css class in bottom toolbar
                            var jqGridBottomToolbarSelector = "div[id=t_" + jqGrid.Id + "]";
                            var jqGridBottomToolbar = $(jqGridBottomToolbarSelector);
                            jqGridBottomToolbar.addClass('vp-grid-status-bar');
                            jqGridBottomToolbar.html("");
                            customStatusBar.appendTo(jqGridBottomToolbar);
                        }

                        //activate search filter toolbar on columns
                        if (vpGridOptions.enableFilterToolbar) {
                            var filterToolbarOptions = { stringResult: true, searchOnEnter: false, defaultSearch: 'cn', ignoreCase: true };
                            if (vpGridOptions.enableOperatorsInFilterToolbar) {
                                filterToolbarOptions.searchOperators = true;
                            }
                            jqGridHost.jqGrid('filterToolbar', filterToolbarOptions);
                            // inject special css tag to be able to customize the rendering of the filterToolbar
                            jqGridRootNode.find("table.ui-search-table").closest("th[role=columnheader]").addClass('vp-grid-filter-toolbar');
                        }

                        //activate frozen column
                        if (jqGrid.hasFrozenColumns) {
                            jqGridHost.jqGrid('setFrozenColumns');
                        }
                        

                        //hide header checkbox if needed
                        if (vpGridOptions.enableMultiSelect === false) {
                            jqGrid.hideHeaderCheckbox();
                        }

                        

                        
                        

                        //TODO : show welcome message window if needed


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initData = function () {
                    try {

                        jqGrid.originalData = [];
                        jqGrid.updatedData = [];

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.initWidth = function () {
                    try {

                        if (jqGrid.hasFrozenColumns) {
                            jqGrid.options.shrinkToFit = false;
                        }

                        if (true) {

                        }

                        if (jqGrid.options.autowidth === true) {
                            return;
                        }

                        // sum all col width
                        var gridWidth = 0;
                        var colModels = jqGrid.options.colModel;
                        var colModelsCount = colModels.length;
                        for (var i = 0; i < colModelsCount; i++) {
                            var colModel = colModels[i];
                            gridWidth += colModel.width;
                        }

                        // left and right column
                        gridWidth += 50;

                        jqGrid.options.width = gridWidth;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }


                function buildColModels() {
                    try {
                        var jqGridSchema = tryFindCurrentGridSchema();

                        if (jqGridSchema.notFound()) {
                            //TODO : log
                            return;
                        }

                        var columsSelector = "tr[role=" + vpGrid.HtmlRoleAttribute.vpGridSchemaColumns + "] ";
                        columsSelector += "th";
                        var jqColumns = jqGridSchema.find(columsSelector);

                        if (jqColumns.notFound()) {
                            vpGrid.logError({
                                message: "Missing description of the excell grid columns. Please insert them in the Web page to activate the Excel grid."
                            });
                        }

                        var colNames = [];
                        var colModel = [];

                        var columnsCount = jqColumns.length;
                        for (var i = 0; i < columnsCount; i++) {
                            var jqColumn = $(jqColumns[i]);
                            var colSchema = {};
                            // get the column title
                            colSchema.title = jqColumn.html();

                            //get the column name
                            colSchema.name = jqColumn.attr(vpGrid.HtmlDataTag.ColumnName);
                            colSchema.index = colSchema.name;

                            //get the column width
                            var minColWidth = jqGrid.options.minColWidth;
                            colSchema.width = jqColumn.toIntOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnWidth, minColWidth)
                            //enforce column min width
                            if (colSchema.width < minColWidth) {
                                colSchema.width = minColWidth;
                            }

                            //get the column hidden attribute
                            colSchema.hidden = jqColumn.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnHidden, false);


                            //get the column frozen attribute
                            colSchema.frozen = jqColumn.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnFrozen, false);
                            if (colSchema.frozen) {
                                jqGrid.hasFrozenColumns = true;
                            }

                            //get the column editable attribute
                            colSchema.editable = jqColumn.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnEditable, false);

                            //get the column edittype attribute
                            // warning : edittype may be overwritten below on specific column Type
                            colSchema.edittype = jqColumn.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnEditType, "text");
                            if (colSchema.edittype === "select") {
                                // create a custom property to flag this column as a select type
                                // this flag will be used to dynamically inject select options in cell editing
                                colSchema.isSelect = true;
                            }

                            if (colSchema.edittype === "autocomplete") {
                                // create a custom property to flag this column as an input textbox with autocomplete
                                // this flag will be used to dynamically inject autocomplete plug-in in cell editing
                                colSchema.edittype = "text";
                                colSchema.isAutocomplete = true;
                            }

                            //setup default search options on columns (used by filterToolbar)
                            colSchema.stype = 'text';
                            colSchema.searchoptions = { sopt: ['cn', 'eq', 'bw', 'bn', 'nc', 'ew', 'en'] };

                            //setup CSS classes
                            var cssClasses = jqColumn.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnCSS, "ui-ellipsis");
                            // always add ui-ellipsis css class
                            if (cssClasses.doesNotContain("ui-ellipsis")) {
                                cssClasses += " ui-ellipsis";
                            }
                            if (colSchema.editable === false) {
                                cssClasses += " vp-grid-not-editable";
                            }
                            if (colSchema.editable === true) {
                                cssClasses += " vp-grid-editable";
                            }
                            colSchema.classes = cssClasses;

                            //get the column align attribute
                            colSchema.align = jqColumn.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnAlign, "left");

                            //get the column type attribute
                            var colType = jqColumn.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnType, "string");
                            if (colType === "checkbox") {
                                colSchema.formatter = "checkbox";
                                colSchema.edittype = "checkbox";
                                // create a custom property to flag this column as checkbox type
                                // this flag will be used to dynamically inject xxx in cell editing
                                colSchema.isCheckbox = true;
                            }

                            if (colType === "note") {
                                colSchema.edittype = "textarea";
                                colSchema.editoptions = { rows: "2", cols: "15" };
                                // create a custom property to flag this column as note type
                                // this flag will be used to dynamically inject xxx in cell editing
                                colSchema.isNote = true;
                            }

                            if (colType === "currency") {
                                colSchema.formatter = "currency";
                                colSchema.formatoptions = jqGrid.defaultCurrencyFormatOptions();
                                
                                colSchema.isCurrency = true;
                                colSchema.editrules = {
                                    number: true,
                                };
                            }

                            if (colType === "int") {
                                colSchema.formatter = "integer";
                                
                                colSchema.isInteger = true;
                                colSchema.editrules = {
                                    integer: true,
                                    minValue: jqColumn.toIntOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnMinValue, null),
                                    maxValue: jqColumn.toIntOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnMaxValue, null),
                                };
                            }

                            if (colType === "link") {
                                colSchema.formatter = jqGrid.linkFormatter;
                                colSchema.unformat = jqGrid.linkUnFormatter;
                                colSchema.formatoptions = { target: '_vpTab' };
                                colSchema.isLink = true;
                            }

                            if (colType === "date") {
                                colSchema.formatter = "date";
                                colSchema.sorttype = "date";
                                // create a custom property to flag this column as date type
                                // this flag will be used to dymacally inject datepicker in cell editing
                                colSchema.isDate = true;
                            }

                            if (colType === "image") {
                                colSchema.formatter = jqGrid.imageFormatter;
                                colSchema.unformat = jqGrid.imageUnformatter;
                                colSchema.isImage = true;
                            }

                            if (colType === "key") {
                                colSchema.isKey = true;
                            }

                            //bind internal vpGrid cell editing validator
                            colSchema.editrules = colSchema.editrules || {};
                            colSchema.editrules.custom = true;
                            colSchema.editrules.custom_func = function (value, colTitle, iCol) {
                                    try {
                                        var validationResult = jqGrid.options.afterValidateCell(value, colTitle, iCol);
                                        return validationResult;
                                    } catch (e) {
                                        vpGrid.logException(e);
                                    }
                                }
                            
                            //build jqGrid options
                            colNames.push(colSchema.title);
                            colModel.push(colSchema);

                        } //end for (var i = 0; i < columnsCount; i++) {

                        jqGrid.options.colNames = colNames;
                        jqGrid.options.colModel = colModel;

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                

                function buildGroupHeadersOptions(element) {
                    try {
                        var selector = "th[" + vpGrid.HtmlDataTag.ColumnName + "]";
                        var jqGroupHeaders = $(element).find(selector);

                        if (jqGroupHeaders.notFound()) {
                            //TODO : log
                            return {};
                        }

                        var groupHeaders = [];

                        var headersCount = jqGroupHeaders.length;
                        for (var i = 0; i < headersCount; i++) {
                            var jqGroupHeader = $(jqGroupHeaders[i]);
                            var headerGroupSchema = {};
                            // get the colspan
                            var numberOfColumns = jqGroupHeader.toIntOrDefaultFromAttribute("colspan", 1);
                            if (numberOfColumns <= 1) {
                                vpGrid.logError({
                                    message: "colspan attribute value is : " + numberOfColumns + " on element : " + outerHtml(jqGroupHeader[0]) + " should be >= 2. This group header is not processed."
                                });
                                continue;
                            }
                            headerGroupSchema.numberOfColumns = numberOfColumns;


                            //get the column name
                            var startColumnName = jqGroupHeader.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.ColumnName, "");
                            if (startColumnName.isNullOrEmptyOrWhitespace()) {
                                vpGrid.logError({
                                    message: "Data attribute : " + vpGrid.HtmlDataTag.ColumnName + " on element : " + outerHtml(jqGroupHeader[0]) + " is empty or invalid. This group header is not processed."
                                });
                                continue;
                            }
                            //TODO : check if startColumnName is a real column

                            headerGroupSchema.startColumnName = startColumnName;

                            // get the title of the group header
                            headerGroupSchema.titleText = jqGroupHeader.html();

                            //build jqGrid group Headers
                            groupHeaders.push(headerGroupSchema);

                        } //end for (var i = 0; i < headersCount; i++)

                        var groupHeadersOptions = {
                            useColSpanStyle: false,
                            groupHeaders: groupHeaders
                        }

                        return groupHeadersOptions;

                    } catch (e) {
                        vpGrid.logException(e);
                        return {}
                    }
                }

                function showWelcomeMessage() {

                }

                function showLoadingMessage() {
                    try {
                        //var jqContainer = vpGrid.currentContainer();

                        //if (jqContainer.notFound()) {
                        //    //TODO : log
                        //    return;
                        //}

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridModalDialog + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        var elementsCount = jqElements.length;
                        for (var i = 0; i < elementsCount; i++) {
                            var element = jqElements[i];
                            var jqElement = $(element);

                            var dialogId = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DialogId, "");
                            if (dialogId !== "vpGridLoading") {
                                continue;
                            }

                            //show modal dialog
                            jqElement.dialog("open");
                            return;

                        } // end for (var i = 0; i < elementsCount; i++)

                        //TODO : log no dialog mode found

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function hideLoadingMessage() {
                    try {
                        //var jqContainer = vpGrid.currentContainer();

                        //if (jqContainer.notFound()) {
                        //    //TODO : log
                        //    return;
                        //}

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridModalDialog + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        var elementsCount = jqElements.length;
                        for (var i = 0; i < elementsCount; i++) {
                            var element = jqElements[i];
                            var jqElement = $(element);

                            var dialogId = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.DialogId, "");
                            if (dialogId !== "vpGridLoading") {
                                continue;
                            }

                            //show modal dialog
                            jqElement.dialog("close");
                            return;

                        } // end for (var i = 0; i < elementsCount; i++)

                        //TODO : log no dialog mode found

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function showEmptyMessageOnLoad() {
                    try {

                        var jqContainer = vpGrid.currentContainer();

                        if (jqContainer.notFound()) {
                            //TODO : log
                            return;
                        }

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridPartialUI + "]";
                        selector += "[" + vpGrid.HtmlDataTag.UIIdentifier + "=vpGridEmptyOnDataLoad" + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        // check if the found element is already in place
                        var isInPlace = jqElements.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.UIInPlace, false);
                        if (isInPlace) {
                            jqElements.showEx();
                            return;
                        }

                        var jqGridContentArea = jqGrid.contentArea();

                        if (jqGridContentArea.notFound()) {
                            vpGrid.logError({
                                functionName: "showEmptyMessageOnLoad",
                                message: "jqGrid content area not found."
                            });
                        }

                        jqElements.appendTo(jqGridContentArea);
                        jqElements.attr(vpGrid.HtmlDataTag.UIInPlace, 'true');
                        jqElements.showEx();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function hideEmptyMessageOnLoad() {
                    try {

                        var jqContainer = vpGrid.currentContainer();

                        if (jqContainer.notFound()) {
                            //TODO : log
                            return;
                        }

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridPartialUI + "]";
                        selector += "[" + vpGrid.HtmlDataTag.UIIdentifier + "=vpGridEmptyOnDataLoad" + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        jqElements.hideEx();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function showEmptyMessageOnGridComplete() {
                    try {

                        var jqContainer = vpGrid.currentContainer();

                        if (jqContainer.notFound()) {
                            //TODO : log
                            return;
                        }

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridPartialUI + "]";
                        selector += "[" + vpGrid.HtmlDataTag.UIIdentifier + "=vpGridEmptyOnGridComplete" + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        // check if the found element is already in place
                        var isInPlace = jqElements.toBooleanOrDefaultFromAttribute(vpGrid.HtmlDataTag.UIInPlace, false);
                        if (isInPlace) {
                            jqElements.showEx();
                            return;
                        }

                        var jqGridContentArea = jqGrid.contentArea();

                        if (jqGridContentArea.notFound()) {
                            vpGrid.logError({
                                functionName: "showEmptyMessageOnGridComplete",
                                message: "jqGrid content area not found."
                            });
                        }

                        jqElements.appendTo(jqGridContentArea);
                        jqElements.attr(vpGrid.HtmlDataTag.UIInPlace, 'true');
                        jqElements.showEx();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function hideEmptyMessageOnGridComplete() {
                    try {

                        var jqContainer = vpGrid.currentContainer();

                        if (jqContainer.notFound()) {
                            //TODO : log
                            return;
                        }

                        var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridPartialUI + "]";
                        selector += "[" + vpGrid.HtmlDataTag.UIIdentifier + "=vpGridEmptyOnGridComplete" + "]";
                        var jqElements = $(selector);

                        if (jqElements.notFound()) {
                            //TODO : log info
                            return;
                        }

                        jqElements.hideEx();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                // row checkbox managment
                jqGrid.lastSelectedCheckboxId = "";
                jqGrid.currentSelectedCheckboxId = "";

                function uncheckRow(checkboxId) {
                    try {
                        if (isNotString(checkboxId)) {
                            vpGrid.logError({
                                functionName: "uncheckRow",
                                message: "checkboxId is not a string"
                            });
                            return;
                        }

                        if (checkboxId.isNullOrEmptyOrWhitespace()) {
                            return;
                        }

                        var checkboxSelector = "input[type=checkbox][id=" + checkboxId + "]";
                        var jqCheckbox = $(checkboxSelector);

                        if (jqCheckbox.notFound()) {
                            vpGrid.logError({
                                functionName: "uncheckRow",
                                message: "Cannot find checkbox : '" + jqCheckbox.selector + "'"
                            });
                            return;
                        }

                        jqCheckbox[0].checked = false;
                        jqCheckbox.removeAttr('checked');
                        jqCheckbox.closest("tr[role=row]").removeClass('ui-state-highlight');

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function checkRow(checkboxId) {
                    try {
                        if (isNotString(checkboxId)) {
                            vpGrid.logError({
                                functionName: "checkRow",
                                message: "checkboxId is not a string"
                            });
                            return;
                        }

                        if (checkboxId.isNullOrEmptyOrWhitespace()) {
                            return;
                        }

                        var checkboxSelector = "input[type=checkbox][id=" + checkboxId + "]";
                        var jqCheckbox = $(checkboxSelector);

                        if (jqCheckbox.notFound()) {
                            vpGrid.logError({
                                functionName: "checkRow",
                                message: "Cannot find checkbox : '" + jqCheckbox.selector + "'"
                            });
                            return;
                        }

                        jqCheckbox[0].checked = true;
                        //jqCheckbox.removeAttr('checked');
                        //jqCheckbox.closest("tr[role=row]").removeClass('ui-state-highlight');

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                function ensureOnlyOneRowCanBeChecked() {
                    try {
                        if (vpGridOptions.enableMultiSelect) {
                            // multi select is authorized
                            return;
                        }

                        //bind click event handler on every checkbox item
                        var jqRowCheckboxes = jqGrid.rowCheckboxes();

                        if (jqRowCheckboxes.notFound()) {
                            //TODO : log
                            return;
                        }

                        jqRowCheckboxes.bind('change', function (e) {
                            try {
                                if (this.checked === false) {
                                    return;
                                }

                                if (isNullOrUndefinedOrEmpty(this.id)) {
                                    return;
                                }

                                if (jqGrid.lastSelectedCheckboxId === this.id) {
                                    return;
                                }

                                var id1 = this.id;
                                var id2 = jqGrid.lastSelectedCheckboxId;
                                executeAsync(function () {
                                    try {
                                        uncheckRow(id2);
                                        checkRow(id1);
                                        jqGrid.currentSelectedCheckboxId = id1;
                                        jqGrid.lastSelectedCheckboxId = id1;
                                    } catch (ex) {
                                        vpGrid.logException(ex);
                                    }
                                }, 100);


                            } catch (ex) {
                                vpGrid.logException(ex);
                            }
                            finally {
                                e.stopPropagation();
                            }
                        });

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }
                // end // row checkbox managment

                jqGrid.bindAutocompleteOnSearchInputs = function () {
                    /// <signature>
                    /// <summary>bind autocomplete widget on search input boxes within the search toolbar</summary>
                    /// </signature>
                    try {
                        // check if search tool bar functionnality  is enabled
                        if (vpGridOptions.enableFilterToolbar === false) {
                            return;
                        }

                        var jqElements = jqGrid.searchToolbarInputs();
                        var count = jqElements.length;
                        for (var i = 0; i < count; i++) {
                            var element = jqElements[i];
                            var jqElement = $(element);
                            var colname = jqElement.toStringOrDefaultFromAttribute("name", "");
                            if (colname.isNullOrEmptyOrWhitespace()) {
                                continue;
                            }
                            var colValues = jqGrid.colValues(colname);
                            if (isNullOrUndefinedOrEmpty(colValues)) {
                                continue;
                            }

                            bindAutoCompleteWidgetWithDataOnElement(element, colValues);

                            // bind to select event to programmatically filter the grid 
                            jqElement.on("autocompleteselect", function (event, ui) {
                                try {
                                    executeAsync(jqGrid.filterGridFromSearchToolbar);

                                } catch (e) {
                                    vpGrid.logException(e);
                                }

                            });


                        }


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.currentColModels = function () {
                    try {
                        // get the jqGrid host table
                        var jqGridHost = jqGrid.host();

                        if (jqGridHost.notFound()) {
                            vpGrid.logError({
                                message: "Cannot get colModels jqGrid object because jqGrid host is not found."
                            });
                            return null;
                        }

                        var colModels = jqGridHost.jqGrid('getGridParam', 'colModel');
                        return colModels;

                    } catch (e) {
                        vpGrid.logException(e);
                        return null;
                    }
                }

                jqGrid.rowData = function (rowId) {
                    try {
                        // get the jqGrid host table
                        var jqGridHost = jqGrid.host();

                        if (jqGridHost.notFound()) {
                            vpGrid.logError({
                                message: "Cannot get rowData object because jqGrid host is not found."
                            });
                            return null;
                        }

                        if (isNotNumber(rowId)) {
                            vpGrid.logError({
                                message: "input parameter is not a number. "
                            });
                            return null;
                        }

                        var data = jqGridHost.jqGrid('getRowData', rowId);
                        return data;

                    } catch (e) {
                        vpGrid.logException(e);
                        return null;
                    }
                }

                jqGrid.rows = function () {
                    /// <signature>
                    /// <summary>Try to find all rows displayed in jqGrid</summary>
                    /// <returns type="Object">Returns the jqGrid table rows found by jQuery</returns>
                    /// </signature>
                    try {

                        var jqGridHost = jqGrid.host();
                        // first step : find all rows in jqGrid                        
                        var rowsSelector = "tr[role=row]";
                        var jqRows = jqGridHost.find(rowsSelector);

                        if (jqRows.notFound()) {
                            vpGrid.logError({
                                message: "Cannot find jqGrid rows because the following selector is not found : '" + jqRows.selector + "'",
                                functionName: "jqGrid.rows"
                            });
                        }

                        //update the total filtered item count
                        jqGrid.filteredItemCount = jqRows.length -1;

                        return jqRows;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.isEmpty = function () {
                    /// <signature>
                    /// <summary>Check if the grid is empty</summary>
                    /// <returns type="Object">Returns true if the grid shows no data</returns>
                    /// </signature>
                    try {

                        var jqGridHost = jqGrid.host();
                        // first step : find all rows in jqGrid                        
                        var rowsSelector = "tr[role=row]";
                        var jqRows = jqGridHost.find(rowsSelector);

                        if (jqRows.notFound()) {
                            vpGrid.logError({
                                message: "Cannot find jqGrid rows because the following selector is not found : '" + jqRows.selector + "'",
                                functionName: "jqGrid.isEmpty"
                            });
                        }

                        if (jqRows.length === 1) {
                            return true;
                        }

                        return false;

                    } catch (e) {
                        vpGrid.logException(e);
                        return false;
                    }
                }

                jqGrid.unselectAllLines = function () {
                    try {
                        var jqRowCheckboxes = jqGrid.rowCheckboxes();

                        if (jqRowCheckboxes.found()) {
                            jqRowCheckboxes.removeAttr('checked');
                        }

                        //unhighlight all lines
                        var jqRows = jqGrid.rows();
                        jqRows.removeClass('ui-state-highlight');

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.rowCheckboxes = function () {
                    /// <signature>
                    /// <summary>Try to find, in jqFrid, all row checkboxes, excluding the one that is in the header</summary>
                    /// <returns type="Object">Returns the jqGrid checkboxes (used for item selection) found by jQuery</returns>
                    /// </signature>
                    try {

                        if (jqGrid.options.multiselect === false) {
                            // there is no checbox configured for row selection
                            return $(null);
                        }

                        var jqRows = jqGrid.rows();
                        // second step : find the cell in edit mode
                        var cellname = "cb";
                        var cellSelector = "td[role=gridcell][aria-describedby=" + jqGrid.Id + "_" + cellname + "]";
                        var jqCells = jqRows.find(cellSelector);
                        if (jqCells.notFound()) {
                            // TODO : log something if multiselect: true
                            return $(null);
                        }

                        //third step : find the input checkbox elements 
                        var jqCheckboxes = jqCells.find("input[type=checkbox]");

                        if (jqCheckboxes.notFound()) {
                            //TODO : log
                        }

                        return jqCheckboxes;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                jqGrid.hideHeaderCheckbox = function () {
                    /// <signature>
                    /// <summary>hide, in jqGrid, the checkbox that is in the header (first column on the left)</summary>
                    /// </signature>
                    try {

                        if (jqGrid.options.multiselect === false) {
                            // there is no checbox configured for row selection
                            return;
                        }

                        var jqGridContainer = jqGrid.container();
                        // first step : find row in jqGrid that hosts the header checkbox                        
                        var rowSelector = "tr[role=rowheader]";
                        var jqRow = jqGridContainer.find(rowSelector);
                        // second step : find the cell in edit mode
                        var cellname = "cb";
                        var cellSelector = "th[id=" + jqGrid.Id + "_" + cellname + "]";
                        var jqCell = jqRow.find(cellSelector);
                        if (jqCell.notFound()) {
                            vpGrid.logError({
                                message: "Cannot find selector : '" + jqCell.selector + "'",
                                functionName: "hideHeaderCheckbox"
                            })
                            return;
                        }

                        //third step : find the input checkbox elements 
                        var jqCheckbox = jqCell.find("input[type=checkbox]");

                        jqCheckbox.addClass('vp-grid-hidden');

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }
                }

                function onRowChecked() {

                }

                jqGrid.filterGridFromSearchToolbar = function () {
                    try {
                        // check if search tool bar functionnality  is enabled
                        if (vpGridOptions.enableFilterToolbar === false) {
                            return;
                        }

                        var jqGridHost = jqGrid.host();

                        if (jqGridHost.notFound()) {
                            vpGrid.logError({
                                message: "Cannot filter jqGrid rows because the following selector is not found : '" + jqGridHost.selector + "'",
                                functionName: "jqGrid.filterGridFromSearchToolbar"
                            });
                            return;
                        }

                        jqGridHost[0].triggerToolbar();

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.searchToolbarInputs = function () {
                    /// <signature>
                    /// <summary>Get all search input boxes within the search toolbar</summary>
                    /// </signature>
                    try {
                        var jqGridContainer = jqGrid.container();
                        if (jqGridContainer.notFound()) {
                            //TODO : log
                            return $(null);
                        }

                        // first step : find row in jqGrid that hosts the search toolbar                        
                        var rowSelector = "tr[role=rowheader].ui-search-toolbar";
                        var jqRow = jqGridContainer.find(rowSelector);

                        //second step : find search input boxes
                        var inputSelector = "input[type=text][id^=gs_]";
                        var jqInput = jqRow.find(inputSelector);
                        if (jqInput.notFound()) {
                            vpGrid.logError({
                                functionName: "jqGrid.searchToolbarInputs",
                                message: "Cannot configure input search toolbar because the following selector was not found : '" + jqInput.selector + "'",
                                info: "Check the version of the jqGrid library."
                            });
                            return $(null);
                        }

                        return jqInput;

                    } catch (e) {
                        vpGrid.logException(e);
                        return $(null);
                    }

                }

                jqGrid.colValues = function (colname) {
                    /// <signature>
                    /// <summary>Get all distinct values which are not null or empty from the named column.</summary>
                    /// <param name="colname" type="String">Name of the column from which to extract all values.</param>
                    /// <returns type="Array">Returns an array of non-null, non-empty, distinct values extracted from the column.Values are sorted asc.</returns>
                    /// </signature>
                    try {
                        if (isNotString(colname)) {
                            //TODO : log
                            return [];
                        }

                        var test = jqGrid.isReady;

                        if (isNullOrUndefined(jqGrid.originalData)) {
                            vpGrid.logError({
                                message: "Cannot get values in column '" + colname + "' because jqGrid.originalData array is not found."
                            });
                        }

                        if (isNotArray(jqGrid.originalData)) {
                            vpGrid.logError({
                                message: "Cannot get values in column '" + colname + "' because jqGrid.originalData is not an array of objects."
                            });
                        }

                        if (jqGrid.originalData.isEmpty()) {
                            return [];
                        }

                        var colValues = [];
                        var data = jqGrid.originalData;
                        var itemCount = data.length;

                        for (var i = 0; i < itemCount; i++) {
                            var dtoObject = data[i];
                            var propertyValue = dtoObject[colname];
                            if (isNullOrUndefined(propertyValue)) {
                                continue;
                            }
                            colValues.push(propertyValue);
                        }

                        var distinctValues = colValues.toUnique();
                        distinctValues = distinctValues.removeEmptyEntries();
                        if (distinctValues.isEmpty()) {
                            return [];
                        }

                        var sortedValues = distinctValues.sort();

                        return sortedValues;

                    } catch (e) {
                        vpGrid.logException(e);
                        return [];
                    }

                }

                jqGrid.loadDataComplete = function (xhr, textStatus) {
                    try {
                        //TODO : check if the grid is empty (because of no data from server)

                        setTimeout(function () {
                            try {
                                hideLoadingMessage();
                                //Show message in grid when no data loaded
                                if (jqGrid.isEmpty()) {
                                    showEmptyMessageOnLoad();
                                    return;
                                }

                                //hide empty message load

                            } catch (e) {
                                vpGrid.logException(e);
                            }
                            
                        }, 1000);


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.loadData = function (timelineExecutionCallback) {
                    try {
                        //step 0 : show modal dialog loading
                        showLoadingMessage();

                        //step1 : gather all remote filters declared on the page
                        var data = jqGrid.remoteFilters();

                        //step2 : fetch data on remote server
                        var ajaxOptions = {
                            cache: false,
                            type: "GET",
                            dataType: "json",
                            success: jqGrid.loadDataFromWebResponse,
                            complete: function (xhr, textStatus) {
                                try {
                                    jqGrid.loadDataComplete(xhr, textStatus);
                                    
                                } catch (e) {
                                    vpGrid.logException(e);
                                }
                                finally {
                                    // continue timeline execution
                                    timelineExecutionCallback();
                                }
                            }
                        }

                        //get url to be requested
                        var crudOperations = vpGridOptions.crudOptions;
                        if (isNullOrUndefinedOrEmpty(crudOperations)) {
                            vpGrid.logError({
                                message: "CRUD operations are not defined in the vpGrid Options object. The grid cannot be loaded."
                            })
                            return;
                        }

                        if (isNullOrUndefinedOrEmpty(crudOperations.read)) {
                            vpGrid.logError({
                                message: "CRUD operation Read is not defined in the vpGrid Options object. The grid cannot be loaded."
                            })
                            return;
                        }

                        var requestUrl = crudOperations.read;
                        ajaxOptions.url = requestUrl;

                        if (isNotNullOrUndefinedOrEmpty(data)) {
                            ajaxOptions.data = data;
                        }

                        $.extend(ajaxOptions, vpGridAjaxOptions);

                        //TODO : raise event to enable listeners inspect and/or modify ajaxOptions

                        $.ajax(ajaxOptions);


                    } catch (e) {
                        vpGrid.logException(e);
                    }
                }

                jqGrid.loadDataFromWebResponse = function (response) {
                    try {
                        if (isNullOrUndefinedOrEmpty(response)) {
                            //TODO : log
                            return;
                        }

                        //TODO : raise event so that listeners can inspect and/or modify response data

                        if (isNullOrUndefined(response.data)) {
                            //TODO : log
                            return;
                        }

                        var data = response.data;
                        var itemCount = data.length;

                        // get the jqGrid host table
                        var jqGridHost = jqGrid.host();

                        //clear grid content
                        jqGridHost.jqGrid('clearGridData', true);

                        //set number of rows in jqGrid
                        var gridOptions = {
                            rowNum: itemCount
                        };
                        jqGridHost.jqGrid('setGridParam', gridOptions);

                        //keep track of received data 
                        jqGrid.originalData = data;

                        //save total item count
                        jqGrid.totalItemCount = itemCount;

                        for (var i = 0; i < itemCount; i++)
                            jqGridHost.jqGrid('addRowData', i + 1, data[i]);

                    } catch (e) {
                        vpGrid.logException(e);
                    }
                    finally {
                        // execute handler onGridLoaded
                        //VP.Timesheet.ExecuteAsync(VP.Timesheet.EventHandlers.LoadGridEvents.onGridLoaded);
                    }
                }

                jqGrid.remoteFilters = function () {
                    try {
                        // get root node of vpGrid
                        var jqRootNode = jqGrid.container();
                        if (jqRootNode.notFound()) {
                            //TODO : log
                            return {};
                        }

                        var selector = "[role=" + vpGrid.HtmlRoleAttribute.RemoteDataFilter + "]";
                        var jqElements = jqRootNode.find(selector);

                        if (jqElements.notFound()) {
                            //TODO : log
                            return {};
                        }

                        var filters = {};
                        var elementsCount = jqElements.length;
                        for (var i = 0; i < elementsCount; i++) {
                            var element = jqElements[i];
                            var jqElement = $(element);

                            //get the name of the filter key
                            var filterKey = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.RemoteDataFilterKey, "Filter");
                            var filterName = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.RemoteDataFilterName, "");
                            var filterOperator = jqElement.toStringOrDefaultFromAttribute(vpGrid.HtmlDataTag.RemoteDataFilterOperator, "eq");
                            var filterValue = jqElement.val();

                            if (isNullOrUndefinedOrEmpty(filterValue)) {
                                // this filter must be ignored
                                continue;
                            }

                            if (filterName.isNullOrEmptyOrWhitespace()) {
                                //TODO : log
                                vpGrid.logError({
                                    message: "data attribute : " + vpGrid.HtmlDataTag.RemoteDataFilterName + " is not defined on element : " + outerHtml(element) + ". This remote data filter is ignored."
                                });
                                continue;
                            }

                            var query = filterName + " " + filterOperator + " " + filterValue;
                            // check if the key has already been setup as a property
                            var filter = {};
                            filter[filterKey] = query;
                            extendOrMerge(filters, filter);

                        } // end for (var i = 0; i < elementsCount; i++)

                        return filters;

                    } catch (e) {
                        vpGrid.logException(e);
                        return {}
                    }
                }

            } catch (e) {
                vpGrid.logException(e);
            }
            finally {
                return jqGrid;
            }

        })(vpGridOptions);

        // end jqGrid layer

        // vpGrid initialization
        vpGrid.currentContainer = function () {
            try {
                var selector = "div[role=" + vpGrid.HtmlRoleAttribute.vpGridContainer + "]";
                var jqElement = $(selector);
                if (jqElement.notFound()) {
                    vpGrid.logError({
                        message: "no container div found (<div role='" + vpGrid.HtmlRoleAttribute.vpGridContainer + "'>" + ") found on page"

                    })
                    return $(null);
                }
                return jqElement;

            } catch (e) {
                vpGrid.logException(e);
                return $(null);
            }

        }

        vpGrid.totalItemCount = function () {
            try {
                var itemCount = JQGrid.totalItemCount;
                return itemCount;

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        vpGrid.filteredItemCount = function () {
            try {
                var itemCount = JQGrid.filteredItemCount;
                return itemCount;

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        vpGrid.init = function (options) {

            try {
                var jqContainer = vpGrid.currentContainer();

                if (jqContainer.notFound()) {
                    vpGrid.logError({
                        message: "vpGrid processing is stopped."
                    })
                    return;
                }

                //merge options
                if (isNotNullOrUndefinedOrEmpty(options)) {
                    $.extend(vpGridOptions, options);
                }

                //sanitize some options
                if (isNullOrUndefined(vpGridOptions.enableCustomCaption)) {
                    vpGridOptions.enableCustomCaption = true;
                }

                registerPrivateHandlers();

                Behaviors.executeTimeline();

            } catch (e) {
                vpGrid.logException(e);
            }
        }

        //end vpGrid initialization

    } catch (e) {
        vpGrid.logException(e);
    }
    finally {
        window.vpGrid = vpGrid;
    }
})(this, jQuery);
